aboutsummaryrefslogtreecommitdiffstats
path: root/misc/yosysjs/demo02.html
blob: 9191db98d42963f8ca830f3a121e523f847261e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<html><head>
	<title>YosysJS Example Application #02</title>
	<script type="text/javascript" src="yosysjs.js"></script>
</head><body>
	<div id="popup" style="position: fixed; left: 0; top: 0; width:100%; height:100%; text-align:center; z-index: 1000;"><div
		style="width:300px; margin: 200px auto; background-color: #88f; border:3px dashed #000;
		padding:15px; text-align:center;"><span id="popupmsg">Loading...</span></div>
	</div>
	<h1>YosysJS Example Application #02</h1>
	<textarea id="code" style="width: 800px; height: 300px;">
// borrowed with some modifications from
// http://www.ee.ed.ac.uk/~gerard/Teach/Verilog/manual/Example/lrgeEx2/cooley.html
module up3down5(clock, data_in, up, down, carry_out, borrow_out, count_out, parity_out);

input [8:0] data_in;
input clock, up, down;

output reg [8:0] count_out;
output reg carry_out, borrow_out, parity_out;

reg [9:0] cnt_up, cnt_dn;
reg [8:0] count_nxt;

always @(posedge clock)
begin
	cnt_dn = count_out - 3'b 101;
	cnt_up = count_out + 2'b 11;

	case ({up,down})
		2'b 00 : count_nxt = data_in;
		2'b 01 : count_nxt = cnt_dn;
		2'b 10 : count_nxt = cnt_up;
		2'b 11 : count_nxt = count_out;
		default : count_nxt = 9'bX;
	endcase

	parity_out  &lt;= ^count_nxt;
	carry_out   &lt;= up &amp; cnt_up[9];
	borrow_out  &lt;= down &amp; cnt_dn[9];
	count_out   &lt;= count_nxt;
end

endmodule
	</textarea><p/>
	<input type="button" value="Before Behavioral Synth" onclick="synth1()">
	<input type="button" value="After Behavioral Synth" onclick="synth2()">
	<input type="button" value="After RTL Synth" onclick="synth3()">
	<input type="button" value="After Gate-Level Synth" onclick="synth4()"><p/>
	<svg id="svg" width="800"></svg>
	</td></tr></table>
	<script type="text/javascript">
		YosysJS.load_viz();
		function on_ys_ready() {
			document.getElementById('popup').style.visibility = 'hidden';
			document.getElementById('popupmsg').textContent = 'Please wait..';
		}
		function handle_run_errors(logmsg, errmsg) {
			if (errmsg != "") {
				window.alert(errmsg);
				document.getElementById('popup').style.visibility = 'hidden';
			}
		}
		function synth1() {
			document.getElementById('popup').style.visibility = 'visible';
			ys.write_file("input.v", document.getElementById('code').value);
			ys.run('design -reset; read_verilog input.v; show -stretch', handle_run_errors);
			ys.read_file('show.dot', (function(text){
				console.log(ys.errmsg);
				if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg');
				document.getElementById('popup').style.visibility = 'hidden';
			}));
		}
		function synth2() {
			document.getElementById('popup').style.visibility = 'visible';
			ys.write_file("input.v", document.getElementById('code').value);
			ys.run('design -reset; read_verilog input.v; proc; opt_clean; show -stretch', handle_run_errors);
			ys.read_file('show.dot', (function(text){
				if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg');
				document.getElementById('popup').style.visibility = 'hidden';
			}));
		}
		function synth3() {
			document.getElementById('popup').style.visibility = 'visible';
			ys.write_file("input.v", document.getElementById('code').value);
			ys.run('design -reset; read_verilog input.v; synth -run coarse; show -stretch', handle_run_errors);
			ys.read_file('show.dot', (function(text){
				if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg');
				document.getElementById('popup').style.visibility = 'hidden';
			}));
		}
		function synth4() {
			document.getElementById('popup').style.visibility = 'visible';
			ys.write_file("input.v", document.getElementById('code').value);
			ys.run('design -reset; read_verilog input.v; synth -run coarse; synth -run fine; show -stretch', handle_run_errors);
			ys.read_file('show.dot', (function(text){
				if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg');
				document.getElementById('popup').style.visibility = 'hidden';
			}));
		}
		var ys = YosysJS.create_worker(on_ys_ready);
		ys.verbose(true);
	</script>
</body></html>
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288
#ifndef CONFIG_VTI
// temporarily in arch/ia64 until can merge into common/grant_table.c
/******************************************************************************
 * common/grant_table.c
 * 
 * Mechanism for granting foreign access to page frames, and receiving
 * page-ownership transfers.
 * 
 * Copyright (c) 2005 Christopher Clark
 * Copyright (c) 2004 K A Fraser
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#define GRANT_DEBUG 0
#define GRANT_DEBUG_VERBOSE 0

#include <xen/config.h>
#include <xen/lib.h>
#include <xen/sched.h>
#include <xen/shadow.h>
#include <xen/mm.h>
#ifdef __ia64__
#define __addr_ok(a) 1	// FIXME-ia64: a variant of access_ok??
// FIXME-ia64: need to implement real cmpxchg_user on ia64
//#define cmpxchg_user(_p,_o,_n) ((*_p == _o) ? ((*_p = _n), 0) : ((_o = *_p), 0))
// FIXME-ia64: these belong in an asm/grant_table.h... PAGE_SIZE different
#undef ORDER_GRANT_FRAMES
//#undef NUM_GRANT_FRAMES
#define ORDER_GRANT_FRAMES 0
//#define NUM_GRANT_FRAMES  (1U << ORDER_GRANT_FRAMES)
#endif

#define PIN_FAIL(_lbl, _rc, _f, _a...)   \
    do {                           \
        DPRINTK( _f, ## _a );      \
        rc = (_rc);                \
        goto _lbl;                 \
    } while ( 0 )

static inline int
get_maptrack_handle(
    grant_table_t *t)
{
    unsigned int h;
    if ( unlikely((h = t->maptrack_head) == t->maptrack_limit) )
        return -1;
    t->maptrack_head = t->maptrack[h].ref_and_flags >> MAPTRACK_REF_SHIFT;
    t->map_count++;
    return h;
}

static inline void
put_maptrack_handle(
    grant_table_t *t, int handle)
{
    t->maptrack[handle].ref_and_flags = t->maptrack_head << MAPTRACK_REF_SHIFT;
    t->maptrack_head = handle;
    t->map_count--;
}

static int
__gnttab_activate_grant_ref(
    struct domain          *mapping_d,          /* IN */
    struct vcpu     *mapping_ed,
    struct domain          *granting_d,
    grant_ref_t             ref,
    u16                     dev_hst_ro_flags,
    unsigned long           host_virt_addr,
    unsigned long          *pframe )            /* OUT */
{
    domid_t               sdom;
    u16                   sflags;
    active_grant_entry_t *act;
    grant_entry_t        *sha;
    s16                   rc = 1;
    unsigned long         frame = 0;
    int                   retries = 0;

    /*
     * Objectives of this function:
     * . Make the record ( granting_d, ref ) active, if not already.
     * . Update shared grant entry of owner, indicating frame is mapped.
     * . Increment the owner act->pin reference counts.
     * . get_page on shared frame if new mapping.
     * . get_page_type if this is first RW mapping of frame.
     * . Add PTE to virtual address space of mapping_d, if necessary.
     * Returns:
     * .  -ve: error
     * .    1: ok
     * .    0: ok and TLB invalidate of host_virt_addr needed.
     *
     * On success, *pframe contains mfn.
     */

    /*
     * We bound the number of times we retry CMPXCHG on memory locations that
     * we share with a guest OS. The reason is that the guest can modify that
     * location at a higher rate than we can read-modify-CMPXCHG, so the guest
     * could cause us to livelock. There are a few cases where it is valid for
     * the guest to race our updates (e.g., to change the GTF_readonly flag),
     * so we allow a few retries before failing.
     */

    act = &granting_d->grant_table->active[ref];
    sha = &granting_d->grant_table->shared[ref];

    spin_lock(&granting_d->grant_table->lock);

    if ( act->pin == 0 )
    {
        /* CASE 1: Activating a previously inactive entry. */

        sflags = sha->flags;
        sdom   = sha->domid;

        for ( ; ; )
        {
            u32 scombo, prev_scombo, new_scombo;

            if ( unlikely((sflags & GTF_type_mask) != GTF_permit_access) ||
                 unlikely(sdom != mapping_d->domain_id) )
                PIN_FAIL(unlock_out, GNTST_general_error,
                         "Bad flags (%x) or dom (%d). (NB. expected dom %d)\n",
                        sflags, sdom, mapping_d->domain_id);

            /* Merge two 16-bit values into a 32-bit combined update. */
            /* NB. Endianness! */
            prev_scombo = scombo = ((u32)sdom << 16) | (u32)sflags;

            new_scombo = scombo | GTF_reading;
            if ( !(dev_hst_ro_flags & GNTMAP_readonly) )
            {
                new_scombo |= GTF_writing;
                if ( unlikely(sflags & GTF_readonly) )
                    PIN_FAIL(unlock_out, GNTST_general_error,
                             "Attempt to write-pin a r/o grant entry.\n");
            }

            /* NB. prev_scombo is updated in place to seen value. */
            if ( unlikely(cmpxchg_user((u32 *)&sha->flags,
                                       prev_scombo,
                                       new_scombo)) )
                PIN_FAIL(unlock_out, GNTST_general_error,
                         "Fault while modifying shared flags and domid.\n");

            /* Did the combined update work (did we see what we expected?). */
            if ( likely(prev_scombo == scombo) )
                break;

            if ( retries++ == 4 )
                PIN_FAIL(unlock_out, GNTST_general_error,
                         "Shared grant entry is unstable.\n");

            /* Didn't see what we expected. Split out the seen flags & dom. */
            /* NB. Endianness! */
            sflags = (u16)prev_scombo;
            sdom   = (u16)(prev_scombo >> 16);
        }

        /* rmb(); */ /* not on x86 */

        frame = __gpfn_to_mfn_foreign(granting_d, sha->frame);

#ifdef __ia64__
// FIXME-ia64: any error checking need to be done here?
#else
        if ( unlikely(!pfn_valid(frame)) ||
             unlikely(!((dev_hst_ro_flags & GNTMAP_readonly) ?
                        get_page(&frame_table[frame], granting_d) :
                        get_page_and_type(&frame_table[frame], granting_d,
                                          PGT_writable_page))) )
        {
            clear_bit(_GTF_writing, &sha->flags);
            clear_bit(_GTF_reading, &sha->flags);
            PIN_FAIL(unlock_out, GNTST_general_error,
                     "Could not pin the granted frame (%lx)!\n", frame);
        }
#endif

        if ( dev_hst_ro_flags & GNTMAP_device_map )
            act->pin += (dev_hst_ro_flags & GNTMAP_readonly) ?
                GNTPIN_devr_inc : GNTPIN_devw_inc;
        if ( dev_hst_ro_flags & GNTMAP_host_map )
            act->pin += (dev_hst_ro_flags & GNTMAP_readonly) ?
                GNTPIN_hstr_inc : GNTPIN_hstw_inc;
        act->domid = sdom;
        act->frame = frame;
    }
    else 
    {
        /* CASE 2: Active modications to an already active entry. */

        /*
         * A cheesy check for possible pin-count overflow.
         * A more accurate check cannot be done with a single comparison.
         */
        if ( (act->pin & 0x80808080U) != 0 )
            PIN_FAIL(unlock_out, ENOSPC,
                     "Risk of counter overflow %08x\n", act->pin);

        frame = act->frame;

        if ( !(dev_hst_ro_flags & GNTMAP_readonly) && 
             !((sflags = sha->flags) & GTF_writing) )
        {
            for ( ; ; )
            {
                u16 prev_sflags;
                
                if ( unlikely(sflags & GTF_readonly) )
                    PIN_FAIL(unlock_out, GNTST_general_error,
                             "Attempt to write-pin a r/o grant entry.\n");

                prev_sflags = sflags;

                /* NB. prev_sflags is updated in place to seen value. */
                if ( unlikely(cmpxchg_user(&sha->flags, prev_sflags, 
                                           prev_sflags | GTF_writing)) )
                    PIN_FAIL(unlock_out, GNTST_general_error,
                         "Fault while modifying shared flags.\n");

                if ( likely(prev_sflags == sflags) )
                    break;

                if ( retries++ == 4 )
                    PIN_FAIL(unlock_out, GNTST_general_error,
                             "Shared grant entry is unstable.\n");

                sflags = prev_sflags;
            }

#ifdef __ia64__
// FIXME-ia64: any error checking need to be done here?
#else
            if ( unlikely(!get_page_type(&frame_table[frame],
                                         PGT_writable_page)) )
            {
                clear_bit(_GTF_writing, &sha->flags);
                PIN_FAIL(unlock_out, GNTST_general_error,
                         "Attempt to write-pin a unwritable page.\n");
            }
#endif
        }

        if ( dev_hst_ro_flags & GNTMAP_device_map )
            act->pin += (dev_hst_ro_flags & GNTMAP_readonly) ? 
                GNTPIN_devr_inc : GNTPIN_devw_inc;

        if ( dev_hst_ro_flags & GNTMAP_host_map )
            act->pin += (dev_hst_ro_flags & GNTMAP_readonly) ?
                GNTPIN_hstr_inc : GNTPIN_hstw_inc;
    }

    /*
     * At this point:
     * act->pin updated to reflect mapping.
     * sha->flags updated to indicate to granting domain mapping done.
     * frame contains the mfn.
     */

    spin_unlock(&granting_d->grant_table->lock);

#ifdef __ia64__
// FIXME-ia64: any error checking need to be done here?
#else
    if ( (host_virt_addr != 0) && (dev_hst_ro_flags & GNTMAP_host_map) )
    {
        /* Write update into the pagetable. */
        l1_pgentry_t pte;
        pte = l1e_from_pfn(frame, _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY);
        if ( !(dev_hst_ro_flags & GNTMAP_readonly) )
            l1e_add_flags(pte,_PAGE_RW);
        rc = update_grant_va_mapping( host_virt_addr, pte, 
                       mapping_d, mapping_ed );

        /*
         * IMPORTANT: (rc == 0) => must flush / invalidate entry in TLB.
         * This is done in the outer gnttab_map_grant_ref.
         */

        if ( rc < 0 )
        {
            /* Failure: undo and abort. */

            spin_lock(&granting_d->grant_table->lock);

            if ( dev_hst_ro_flags & GNTMAP_readonly )
            {
                act->pin -= GNTPIN_hstr_inc;
            }
            else
            {
                act->pin -= GNTPIN_hstw_inc;
                if ( (act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask)) == 0 )
                {
                    clear_bit(_GTF_writing, &sha->flags);
                    put_page_type(&frame_table[frame]);
                }
            }

            if ( act->pin == 0 )
            {
                clear_bit(_GTF_reading, &sha->flags);
                put_page(&frame_table[frame]);
            }

            spin_unlock(&granting_d->grant_table->lock);
        }

    }
#endif

    *pframe = frame;
    return rc;

 unlock_out:
    spin_unlock(&granting_d->grant_table->lock);
    return rc;
}

/*
 * Returns 0 if TLB flush / invalidate required by caller.
 * va will indicate the address to be invalidated.
 */
static int
__gnttab_map_grant_ref(
    gnttab_map_grant_ref_t *uop,
    unsigned long *va)
{
    domid_t               dom;
    grant_ref_t           ref;
    struct domain        *ld, *rd;
    struct vcpu   *led;
    u16                   dev_hst_ro_flags;
    int                   handle;
    unsigned long         frame = 0, host_virt_addr;
    int                   rc;

    led = current;
    ld = led->domain;

    /* Bitwise-OR avoids short-circuiting which screws control flow. */
    if ( unlikely(__get_user(dom, &uop->dom) |
                  __get_user(ref, &uop->ref) |
                  __get_user(host_virt_addr, &uop->host_addr) |
                  __get_user(dev_hst_ro_flags, &uop->flags)) )
    {
        DPRINTK("Fault while reading gnttab_map_grant_ref_t.\n");
        return -EFAULT; /* don't set status */
    }


    if ( ((host_virt_addr != 0) || (dev_hst_ro_flags & GNTMAP_host_map)) &&
         unlikely(!__addr_ok(host_virt_addr)))
    {
        DPRINTK("Bad virtual address (%lx) or flags (%x).\n",
                host_virt_addr, dev_hst_ro_flags);
        (void)__put_user(GNTST_bad_virt_addr, &uop->handle);
        return GNTST_bad_gntref;
    }

    if ( unlikely(ref >= NR_GRANT_ENTRIES) ||
         unlikely((dev_hst_ro_flags &
                   (GNTMAP_device_map|GNTMAP_host_map)) == 0) )
    {
        DPRINTK("Bad ref (%d) or flags (%x).\n", ref, dev_hst_ro_flags);
        (void)__put_user(GNTST_bad_gntref, &uop->handle);
        return GNTST_bad_gntref;
    }

    if ( unlikely((rd = find_domain_by_id(dom)) == NULL) ||
         unlikely(ld == rd) )
    {
        if ( rd != NULL )
            put_domain(rd);
        DPRINTK("Could not find domain %d\n", dom);
        (void)__put_user(GNTST_bad_domain, &uop->handle);
        return GNTST_bad_domain;
    }

    /* Get a maptrack handle. */
    if ( unlikely((handle = get_maptrack_handle(ld->grant_table)) == -1) )
    {
        int              i;
        grant_mapping_t *new_mt;
        grant_table_t   *lgt      = ld->grant_table;

        /* Grow the maptrack table. */
        new_mt = alloc_xenheap_pages(lgt->maptrack_order + 1);
        if ( new_mt == NULL )
        {
            put_domain(rd);
            DPRINTK("No more map handles available\n");
            (void)__put_user(GNTST_no_device_space, &uop->handle);
            return GNTST_no_device_space;
        }

        memcpy(new_mt, lgt->maptrack, PAGE_SIZE << lgt->maptrack_order);
        for ( i = lgt->maptrack_limit; i < (lgt->maptrack_limit << 1); i++ )
            new_mt[i].ref_and_flags = (i+1) << MAPTRACK_REF_SHIFT;

        free_xenheap_pages(lgt->maptrack, lgt->maptrack_order);
        lgt->maptrack          = new_mt;
        lgt->maptrack_order   += 1;
        lgt->maptrack_limit  <<= 1;

        printk("Doubled maptrack size\n");
        handle = get_maptrack_handle(ld->grant_table);
    }

#if GRANT_DEBUG_VERBOSE
    DPRINTK("Mapping grant ref (%hu) for domain (%hu) with flags (%x)\n",
            ref, dom, dev_hst_ro_flags);
#endif

    if ( 0 <= ( rc = __gnttab_activate_grant_ref( ld, led, rd, ref,
                                                  dev_hst_ro_flags,
                                                  host_virt_addr, &frame)))
    {
        /*
         * Only make the maptrack live _after_ writing the pte, in case we 
         * overwrite the same frame number, causing a maptrack walk to find it
         */
        ld->grant_table->maptrack[handle].domid = dom;

        ld->grant_table->maptrack[handle].ref_and_flags
            = (ref << MAPTRACK_REF_SHIFT) |
              (dev_hst_ro_flags & MAPTRACK_GNTMAP_MASK);

        (void)__put_user(frame, &uop->dev_bus_addr);

        if ( dev_hst_ro_flags & GNTMAP_host_map )
            *va = host_virt_addr;

        (void)__put_user(handle, &uop->handle);
    }
    else
    {
        (void)__put_user(rc, &uop->handle);
        put_maptrack_handle(ld->grant_table, handle);
    }

    put_domain(rd);
    return rc;
}

static long
gnttab_map_grant_ref(
    gnttab_map_grant_ref_t *uop, unsigned int count)
{
    int i, flush = 0;
    unsigned long va = 0;

    for ( i = 0; i < count; i++ )
        if ( __gnttab_map_grant_ref(&uop[i], &va) == 0 )
            flush++;

#ifdef __ia64__
// FIXME-ia64: probably need to do something here to avoid stale mappings?
#else
    if ( flush == 1 )
        flush_tlb_one_mask(current->domain->cpumask, va);
    else if ( flush != 0 ) 
        flush_tlb_mask(current->domain->cpumask);
#endif

    return 0;
}

static int
__gnttab_unmap_grant_ref(
    gnttab_unmap_grant_ref_t *uop,
    unsigned long *va)
{
    domid_t        dom;
    grant_ref_t    ref;
    u16            handle;
    struct domain *ld, *rd;

    active_grant_entry_t *act;
    grant_entry_t *sha;
    grant_mapping_t *map;
    u16            flags;
    s16            rc = 1;
    unsigned long  frame, virt;

    ld = current->domain;

    /* Bitwise-OR avoids short-circuiting which screws control flow. */
    if ( unlikely(__get_user(virt, &uop->host_addr) |
                  __get_user(frame, &uop->dev_bus_addr) |
                  __get_user(handle, &uop->handle)) )
    {
        DPRINTK("Fault while reading gnttab_unmap_grant_ref_t.\n");
        return -EFAULT; /* don't set status */
    }

    map = &ld->grant_table->maptrack[handle];

    if ( unlikely(handle >= ld->grant_table->maptrack_limit) ||
         unlikely(!(map->ref_and_flags & MAPTRACK_GNTMAP_MASK)) )
    {
        DPRINTK("Bad handle (%d).\n", handle);
        (void)__put_user(GNTST_bad_handle, &uop->status);
        return GNTST_bad_handle;
    }

    dom   = map->domid;
    ref   = map->ref_and_flags >> MAPTRACK_REF_SHIFT;
    flags = map->ref_and_flags & MAPTRACK_GNTMAP_MASK;

    if ( unlikely((rd = find_domain_by_id(dom)) == NULL) ||
         unlikely(ld == rd) )
    {
        if ( rd != NULL )
            put_domain(rd);
        DPRINTK("Could not find domain %d\n", dom);
        (void)__put_user(GNTST_bad_domain, &uop->status);
        return GNTST_bad_domain;
    }

#if GRANT_DEBUG_VERBOSE
    DPRINTK("Unmapping grant ref (%hu) for domain (%hu) with handle (%hu)\n",
            ref, dom, handle);
#endif

    act = &rd->grant_table->active[ref];
    sha = &rd->grant_table->shared[ref];

    spin_lock(&rd->grant_table->lock);

    if ( frame == 0 )
    {
        frame = act->frame;
    }
    else
    {
        if ( unlikely(frame != act->frame) )
            PIN_FAIL(unmap_out, GNTST_general_error,
                     "Bad frame number doesn't match gntref.\n");
        if ( flags & GNTMAP_device_map )
            act->pin -= (flags & GNTMAP_readonly) ? GNTPIN_devr_inc
                                                  : GNTPIN_devw_inc;

        map->ref_and_flags &= ~GNTMAP_device_map;
        (void)__put_user(0, &uop->dev_bus_addr);

        /* Frame is now unmapped for device access. */
    }

    if ( (virt != 0) &&
         (flags & GNTMAP_host_map) &&
         ((act->pin & (GNTPIN_hstw_mask | GNTPIN_hstr_mask)) > 0))
    {
#ifdef __ia64__
// FIXME-ia64: any error checking need to be done here?
#else
        l1_pgentry_t   *pl1e;
        unsigned long   _ol1e;

        pl1e = &linear_pg_table[l1_linear_offset(virt)];

        if ( unlikely(__get_user(_ol1e, (unsigned long *)pl1e) != 0) )
        {
            DPRINTK("Could not find PTE entry for address %lx\n", virt);
            rc = -EINVAL;
            goto unmap_out;
        }

        /*
         * Check that the virtual address supplied is actually mapped to 
         * act->frame.
         */
        if ( unlikely((_ol1e >> PAGE_SHIFT) != frame ))
        {
            DPRINTK("PTE entry %lx for address %lx doesn't match frame %lx\n",
                    _ol1e, virt, frame);
            rc = -EINVAL;
            goto unmap_out;
        }

        /* Delete pagetable entry. */
        if ( unlikely(__put_user(0, (unsigned long *)pl1e)))
        {
            DPRINTK("Cannot delete PTE entry at %p for virtual address %lx\n",
                    pl1e, virt);
            rc = -EINVAL;
            goto unmap_out;
        }
#endif

        map->ref_and_flags &= ~GNTMAP_host_map;

        act->pin -= (flags & GNTMAP_readonly) ? GNTPIN_hstr_inc
                                              : GNTPIN_hstw_inc;

        rc = 0;
        *va = virt;
    }

    if ( (map->ref_and_flags & (GNTMAP_device_map|GNTMAP_host_map)) == 0)
    {
        map->ref_and_flags = 0;
        put_maptrack_handle(ld->grant_table, handle);
    }

#ifdef __ia64__
// FIXME-ia64: any error checking need to be done here?  I think not and then
//  this can probably be macro-ized into nothingness
#else
    /* If just unmapped a writable mapping, mark as dirtied */
    if ( unlikely(shadow_mode_log_dirty(rd)) &&
        !( flags & GNTMAP_readonly ) )
         mark_dirty(rd, frame);
#endif

    /* If the last writable mapping has been removed, put_page_type */
    if ( ( (act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask) ) == 0) &&
         ( !( flags & GNTMAP_readonly ) ) )
    {
        clear_bit(_GTF_writing, &sha->flags);
        put_page_type(&frame_table[frame]);
    }

    if ( act->pin == 0 )
    {
        clear_bit(_GTF_reading, &sha->flags);
        put_page(&frame_table[frame]);
    }

 unmap_out:
    (void)__put_user(rc, &uop->status);
    spin_unlock(&rd->grant_table->lock);
    put_domain(rd);
    return rc;
}

static long
gnttab_unmap_grant_ref(
    gnttab_unmap_grant_ref_t *uop, unsigned int count)
{
    int i, flush = 0;
    unsigned long va = 0;

    for ( i = 0; i < count; i++ )
        if ( __gnttab_unmap_grant_ref(&uop[i], &va) == 0 )
            flush++;

#ifdef __ia64__
// FIXME-ia64: probably need to do something here to avoid stale mappings?
#else
    if ( flush == 1 )
        flush_tlb_one_mask(current->domain->cpumask, va);
    else if ( flush != 0 ) 
        flush_tlb_mask(current->domain->cpumask);
#endif

    return 0;
}

static long 
gnttab_setup_table(
    gnttab_setup_table_t *uop, unsigned int count)
{
    gnttab_setup_table_t  op;
    struct domain        *d;
    int                   i;
    unsigned long addr;

    if ( count != 1 )
        return -EINVAL;

    if ( unlikely(copy_from_user(&op, uop, sizeof(op)) != 0) )
    {
        DPRINTK("Fault while reading gnttab_setup_table_t.\n");
        return -EFAULT;
    }

    if ( unlikely(op.nr_frames > NR_GRANT_FRAMES) )
    {
        DPRINTK("Xen only supports up to %d grant-table frames per domain.\n",
                NR_GRANT_FRAMES);
        (void)put_user(GNTST_general_error, &uop->status);
        return 0;
    }

    if ( op.dom == DOMID_SELF )
    {
        op.dom = current->domain->domain_id;
    }
    else if ( unlikely(!IS_PRIV(current->domain)) )
    {
        (void)put_user(GNTST_permission_denied, &uop->status);
        return 0;
    }

    if ( unlikely((d = find_domain_by_id(op.dom)) == NULL) )
    {
        DPRINTK("Bad domid %d.\n", op.dom);
        (void)put_user(GNTST_bad_domain, &uop->status);
        return 0;
    }

    if ( op.nr_frames <= NR_GRANT_FRAMES )
    {
        ASSERT(d->grant_table != NULL);
        (void)put_user(GNTST_okay, &uop->status);
#ifdef __ia64__
	if (d == dom0) {
            for ( i = 0; i < op.nr_frames; i++ )
                (void)put_user(
                    (virt_to_phys(d->grant_table->shared) >> PAGE_SHIFT) + i,
                    &uop->frame_list[i]);
	} else {
            /* IA64 hack - need to map it somewhere */
            addr = (1UL << 40);
            map_domain_page(d, addr, virt_to_phys(d->grant_table->shared));
            (void)put_user(addr >> PAGE_SHIFT, &uop->frame_list[0]);
        }
#else
        for ( i = 0; i < op.nr_frames; i++ )
            (void)put_user(
                (virt_to_phys(d->grant_table->shared) >> PAGE_SHIFT) + i,
                &uop->frame_list[i]);
#endif
    }

    put_domain(d);
    return 0;
}

#if GRANT_DEBUG
static int
gnttab_dump_table(gnttab_dump_table_t *uop)
{
    grant_table_t        *gt;
    gnttab_dump_table_t   op;
    struct domain        *d;
    u32                   shared_mfn;
    active_grant_entry_t *act;
    grant_entry_t         sha_copy;
    grant_mapping_t      *maptrack;
    int                   i;


    if ( unlikely(copy_from_user(&op, uop, sizeof(op)) != 0) )
    {
        DPRINTK("Fault while reading gnttab_dump_table_t.\n");
        return -EFAULT;
    }

    if ( op.dom == DOMID_SELF )
    {
        op.dom = current->domain->domain_id;
    }

    if ( unlikely((d = find_domain_by_id(op.dom)) == NULL) )
    {
        DPRINTK("Bad domid %d.\n", op.dom);
        (void)put_user(GNTST_bad_domain, &uop->status);
        return 0;
    }

    ASSERT(d->grant_table != NULL);
    gt = d->grant_table;
    (void)put_user(GNTST_okay, &uop->status);

    shared_mfn = virt_to_phys(d->grant_table->shared);

    DPRINTK("Grant table for dom (%hu) MFN (%x)\n",
            op.dom, shared_mfn);

    ASSERT(d->grant_table->active != NULL);
    ASSERT(d->grant_table->shared != NULL);
    ASSERT(d->grant_table->maptrack != NULL);

    for ( i = 0; i < NR_GRANT_ENTRIES; i++ )
    {
        sha_copy =  gt->shared[i];

        if ( sha_copy.flags )
        {
            DPRINTK("Grant: dom (%hu) SHARED (%d) flags:(%hx) "
                    "dom:(%hu) frame:(%lx)\n",
                    op.dom, i, sha_copy.flags, sha_copy.domid, sha_copy.frame);
        }
    }

    spin_lock(&gt->lock);

    for ( i = 0; i < NR_GRANT_ENTRIES; i++ )
    {
        act = &gt->active[i];

        if ( act->pin )
        {
            DPRINTK("Grant: dom (%hu) ACTIVE (%d) pin:(%x) "
                    "dom:(%hu) frame:(%lx)\n",
                    op.dom, i, act->pin, act->domid, act->frame);
        }
    }

    for ( i = 0; i < gt->maptrack_limit; i++ )
    {
        maptrack = &gt->maptrack[i];

        if ( maptrack->ref_and_flags & MAPTRACK_GNTMAP_MASK )
        {
            DPRINTK("Grant: dom (%hu) MAP (%d) ref:(%hu) flags:(%x) "
                    "dom:(%hu)\n",
                    op.dom, i,
                    maptrack->ref_and_flags >> MAPTRACK_REF_SHIFT,
                    maptrack->ref_and_flags & MAPTRACK_GNTMAP_MASK,
                    maptrack->domid);
        }
    }

    spin_unlock(&gt->lock);

    put_domain(d);
    return 0;
}
#endif

long 
do_grant_table_op(
    unsigned int cmd, void *uop, unsigned int count)
{
    long rc;

    if ( count > 512 )
        return -EINVAL;

    LOCK_BIGLOCK(current->domain);

    rc = -EFAULT;
    switch ( cmd )
    {
    case GNTTABOP_map_grant_ref:
        if ( unlikely(!array_access_ok(
            uop, count, sizeof(gnttab_map_grant_ref_t))) )
            goto out;
        rc = gnttab_map_grant_ref((gnttab_map_grant_ref_t *)uop, count);
        break;
    case GNTTABOP_unmap_grant_ref:
        if ( unlikely(!array_access_ok(
            uop, count, sizeof(gnttab_unmap_grant_ref_t))) )
            goto out;
        rc = gnttab_unmap_grant_ref((gnttab_unmap_grant_ref_t *)uop, count);
        break;
    case GNTTABOP_setup_table:
        rc = gnttab_setup_table((gnttab_setup_table_t *)uop, count);
        break;
#if GRANT_DEBUG
    case GNTTABOP_dump_table:
        rc = gnttab_dump_table((gnttab_dump_table_t *)uop);
        break;
#endif
    default:
        rc = -ENOSYS;
        break;
    }

out:
    UNLOCK_BIGLOCK(current->domain);

    return rc;
}

int
gnttab_check_unmap(
    struct domain *rd, struct domain *ld, unsigned long frame, int readonly)
{
    /* Called when put_page is invoked on a page belonging to a foreign domain.
     * Instead of decrementing the frame table ref count, locate the grant
     * table entry, if any, and if found, decrement that count.
     * Called a _lot_ at domain creation because pages mapped by priv domains
     * also traverse this.
     */

    /* Note: If the same frame is mapped multiple times, and then one of
     *       the ptes is overwritten, which maptrack handle gets invalidated?
     * Advice: Don't do it. Explicitly unmap.
     */

    unsigned int handle, ref, refcount;
    grant_table_t        *lgt, *rgt;
    active_grant_entry_t *act;
    grant_mapping_t      *map;
    int found = 0;

    lgt = ld->grant_table;

#if GRANT_DEBUG_VERBOSE
    if ( ld->domain_id != 0 )
    {
        DPRINTK("Foreign unref rd(%d) ld(%d) frm(%x) flgs(%x).\n",
                rd->domain_id, ld->domain_id, frame, readonly);
    }
#endif

    /* Fast exit if we're not mapping anything using grant tables */
    if ( lgt->map_count == 0 )
        return 0;

    if ( get_domain(rd) == 0 )
    {
        DPRINTK("gnttab_check_unmap: couldn't get_domain rd(%d)\n",
                rd->domain_id);
        return 0;
    }

    rgt = rd->grant_table;

    for ( handle = 0; handle < lgt->maptrack_limit; handle++ )
    {
        map = &lgt->maptrack[handle];

        if ( ( map->ref_and_flags & MAPTRACK_GNTMAP_MASK ) &&
             ( readonly ? 1 : (!(map->ref_and_flags & GNTMAP_readonly))))
        {
            ref = (map->ref_and_flags >> MAPTRACK_REF_SHIFT);
            act = &rgt->active[ref];

            spin_lock(&rgt->lock);

            if ( act->frame != frame )
            {
                spin_unlock(&rgt->lock);
                continue;
            }

            refcount = act->pin & ( readonly ? GNTPIN_hstr_mask
                                             : GNTPIN_hstw_mask );
            if ( refcount == 0 )
            {
                spin_unlock(&rgt->lock);
                continue;
            }

            /* gotcha */
            DPRINTK("Grant unref rd(%d) ld(%d) frm(%lx) flgs(%x).\n",
                    rd->domain_id, ld->domain_id, frame, readonly);

            if ( readonly )
                act->pin -= GNTPIN_hstr_inc;
            else
            {
                act->pin -= GNTPIN_hstw_inc;

                /* any more granted writable mappings? */
                if ( (act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask)) == 0 )
                {
                    clear_bit(_GTF_writing, &rgt->shared[ref].flags);
                    put_page_type(&frame_table[frame]);
                }
            }

            if ( act->pin == 0 )
            {
                clear_bit(_GTF_reading, &rgt->shared[ref].flags);
                put_page(&frame_table[frame]);
            }
            spin_unlock(&rgt->lock);

            clear_bit(GNTMAP_host_map, &map->ref_and_flags);

            if ( !(map->ref_and_flags & GNTMAP_device_map) )
                put_maptrack_handle(lgt, handle);

            found = 1;
            break;
        }
    }
    put_domain(rd);

    return found;
}

int 
gnttab_prepare_for_transfer(
    struct domain *rd, struct domain *ld, grant_ref_t ref)
{
    grant_table_t *rgt;
    grant_entry_t *sha;
    domid_t        sdom;
    u16            sflags;
    u32            scombo, prev_scombo;
    int            retries = 0;
    unsigned long  target_pfn;

    DPRINTK("gnttab_prepare_for_transfer rd(%hu) ld(%hu) ref(%hu).\n",
            rd->domain_id, ld->domain_id, ref);

    if ( unlikely((rgt = rd->grant_table) == NULL) ||
         unlikely(ref >= NR_GRANT_ENTRIES) )
    {
        DPRINTK("Dom %d has no g.t., or ref is bad (%d).\n",
                rd->domain_id, ref);
        return 0;
    }

    spin_lock(&rgt->lock);

    sha = &rgt->shared[ref];
    
    sflags = sha->flags;
    sdom   = sha->domid;

    for ( ; ; )
    {
        target_pfn = sha->frame;

        if ( unlikely(target_pfn >= max_page ) )
        {
            DPRINTK("Bad pfn (%lx)\n", target_pfn);
            goto fail;
        }

        if ( unlikely(sflags != GTF_accept_transfer) ||
             unlikely(sdom != ld->domain_id) )
        {
            DPRINTK("Bad flags (%x) or dom (%d). (NB. expected dom %d)\n",
                    sflags, sdom, ld->domain_id);
            goto fail;
        }

        /* Merge two 16-bit values into a 32-bit combined update. */
        /* NB. Endianness! */
        prev_scombo = scombo = ((u32)sdom << 16) | (u32)sflags;

        /* NB. prev_scombo is updated in place to seen value. */
        if ( unlikely(cmpxchg_user((u32 *)&sha->flags, prev_scombo, 
                                   prev_scombo | GTF_transfer_committed)) )
        {
            DPRINTK("Fault while modifying shared flags and domid.\n");
            goto fail;
        }

        /* Did the combined update work (did we see what we expected?). */
        if ( likely(prev_scombo == scombo) )
            break;

        if ( retries++ == 4 )
        {
            DPRINTK("Shared grant entry is unstable.\n");
            goto fail;
        }

        /* Didn't see what we expected. Split out the seen flags & dom. */
        /* NB. Endianness! */
        sflags = (u16)prev_scombo;
        sdom   = (u16)(prev_scombo >> 16);
    }

    spin_unlock(&rgt->lock);
    return 1;

 fail:
    spin_unlock(&rgt->lock);
    return 0;
}

void 
gnttab_notify_transfer(
    struct domain *rd, struct domain *ld, grant_ref_t ref, unsigned long frame)
{
    grant_entry_t  *sha;
    unsigned long   pfn;

    DPRINTK("gnttab_notify_transfer rd(%hu) ld(%hu) ref(%hu).\n",
            rd->domain_id, ld->domain_id, ref);

    sha = &rd->grant_table->shared[ref];

    spin_lock(&rd->grant_table->lock);

#ifdef __ia64__
// FIXME-ia64: any error checking need to be done here?
#else
    pfn = sha->frame;

    if ( unlikely(pfn >= max_page ) )
        DPRINTK("Bad pfn (%lx)\n", pfn);
    else
    {
        machine_to_phys_mapping[frame] = pfn;

        if ( unlikely(shadow_mode_log_dirty(ld)))
             mark_dirty(ld, frame);

        if (shadow_mode_translate(ld))
            __phys_to_machine_mapping[pfn] = frame;
    }
#endif
    sha->frame = __mfn_to_gpfn(rd, frame);
    sha->domid = rd->domain_id;
    wmb();
    sha->flags = ( GTF_accept_transfer | GTF_transfer_completed );

    spin_unlock(&rd->grant_table->lock);

    return;
}

int 
grant_table_create(
    struct domain *d)
{
    grant_table_t *t;
    int            i;

    if ( (t = xmalloc(grant_table_t)) == NULL )
        goto no_mem;

    /* Simple stuff. */
    memset(t, 0, sizeof(*t));
    spin_lock_init(&t->lock);

    /* Active grant table. */
    if ( (t->active = xmalloc_array(active_grant_entry_t, NR_GRANT_ENTRIES))
         == NULL )
        goto no_mem;
    memset(t->active, 0, sizeof(active_grant_entry_t) * NR_GRANT_ENTRIES);

    /* Tracking of mapped foreign frames table */
    if ( (t->maptrack = alloc_xenheap_page()) == NULL )
        goto no_mem;
    t->maptrack_order = 0;
    t->maptrack_limit = PAGE_SIZE / sizeof(grant_mapping_t);
    memset(t->maptrack, 0, PAGE_SIZE);
    for ( i = 0; i < t->maptrack_limit; i++ )
        t->maptrack[i].ref_and_flags = (i+1) << MAPTRACK_REF_SHIFT;

    /* Shared grant table. */
    t->shared = alloc_xenheap_pages(ORDER_GRANT_FRAMES);
    if ( t->shared == NULL )
        goto no_mem;
    memset(t->shared, 0, NR_GRANT_FRAMES * PAGE_SIZE);

#ifdef __ia64__
// I don't think there's anything to do here on ia64?...
#else
    for ( i = 0; i < NR_GRANT_FRAMES; i++ )
    {
        SHARE_PFN_WITH_DOMAIN(
            virt_to_page((char *)(t->shared)+(i*PAGE_SIZE)), d);
        machine_to_phys_mapping[(virt_to_phys(t->shared) >> PAGE_SHIFT) + i] =
            INVALID_M2P_ENTRY;
    }
#endif

    /* Okay, install the structure. */
    wmb(); /* avoid races with lock-free access to d->grant_table */
    d->grant_table = t;
    return 0;

 no_mem:
    if ( t != NULL )
    {
        xfree(t->active);
        if ( t->maptrack != NULL )
            free_xenheap_page(t->maptrack);
        xfree(t);
    }
    return -ENOMEM;
}

void
gnttab_release_dev_mappings(grant_table_t *gt)
{
    grant_mapping_t        *map;
    domid_t                 dom;
    grant_ref_t             ref;
    u16                     handle;
    struct domain          *ld, *rd;
    unsigned long           frame;
    active_grant_entry_t   *act;
    grant_entry_t          *sha;

    ld = current->domain;

    for ( handle = 0; handle < gt->maptrack_limit; handle++ )
    {
        map = &gt->maptrack[handle];

        if ( map->ref_and_flags & GNTMAP_device_map )
        {
            dom = map->domid;
            ref = map->ref_and_flags >> MAPTRACK_REF_SHIFT;

            DPRINTK("Grant release (%hu) ref:(%hu) flags:(%x) dom:(%hu)\n",
                    handle, ref,
                    map->ref_and_flags & MAPTRACK_GNTMAP_MASK, dom);

            if ( unlikely((rd = find_domain_by_id(dom)) == NULL) ||
                 unlikely(ld == rd) )
            {
                if ( rd != NULL )
                    put_domain(rd);

                printk(KERN_WARNING "Grant release: No dom%d\n", dom);
                continue;
            }

            act = &rd->grant_table->active[ref];
            sha = &rd->grant_table->shared[ref];

            spin_lock(&rd->grant_table->lock);

            if ( act->pin & (GNTPIN_devw_mask | GNTPIN_devr_mask) )
            {
                frame = act->frame;

                if ( ( (act->pin & GNTPIN_hstw_mask) == 0 ) &&
                     ( (act->pin & GNTPIN_devw_mask) >  0 ) )
                {
                    clear_bit(_GTF_writing, &sha->flags);
                    put_page_type(&frame_table[frame]);
                }

                act->pin &= ~(GNTPIN_devw_mask | GNTPIN_devr_mask);

                if ( act->pin == 0 )
                {
                    clear_bit(_GTF_reading, &sha->flags);
                    map->ref_and_flags = 0;
                    put_page(&frame_table[frame]);
                }
                else
                    map->ref_and_flags &= ~GNTMAP_device_map;
            }

            spin_unlock(&rd->grant_table->lock);

            put_domain(rd);
        }
    }
}


void
grant_table_destroy(
    struct domain *d)
{
    grant_table_t *t;

    if ( (t = d->grant_table) != NULL )
    {
        /* Free memory relating to this grant table. */
        d->grant_table = NULL;
        free_xenheap_pages(t->shared, ORDER_GRANT_FRAMES);
        free_xenheap_page(t->maptrack);
        xfree(t->active);
        xfree(t);
    }
}

void
grant_table_init(
    void)
{
    /* Nothing. */
}
#endif

/*
 * Local variables:
 * mode: C
 * c-set-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */