Loading...
   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
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
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
/*
 * Copyright (c) 2024 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
#ifndef _VM_VM_MAP_LOCK_H_
#define _VM_VM_MAP_LOCK_H_

#include <stdbool.h>
#include <kern/locks.h>
#include <kern/lock_mtx.h>
#include <vm/vm_map_internal.h>

__BEGIN_DECLS


#if DEVELOPMENT || DEBUG
#define RANGE_LOCK_DEBUG
#endif /* DEVELOPMENT || DEBUG */

#ifdef RANGE_LOCK_DEBUG
#define RANGE_LOCK_ASSERT(cond) assert(cond)
#else
#define RANGE_LOCK_ASSERT(cond)
#endif

#define err_vm_map_lock(e)              (err_vm | err_sub(2) | (e))
/*! denote in a preflight hook that this entry shouldn't be prepared */
#define VMRL_ERR_SKIP_PREPARE           err_vm_map_lock(1)
/*! denote in a preflight hook that the caller wants to wait for unwire */
#define VMRL_ERR_WAIT_FOR_KUNWIRE       err_vm_map_lock(2)
/*! denote in a preflight hook that the caller wants symmetric COW setup */
#define VMRL_ERR_SETUP_SYMMETRIC_COW    err_vm_map_lock(3)
/*! same as VMRL_ERR_SETUP_SYMMETRIC_COW, but the entry on which COW is setup is not clipped */
#define VMRL_ERR_SETUP_SYMMETRIC_COW_NOCLIP   err_vm_map_lock(4)
/*! denote in a preflight hook that the entry is going to be shared with another map */
#define VMRL_ERR_PREPARE_FOR_SHARE_NOCLIP   err_vm_map_lock(5)
/*! denote in a preflight hook that the entry is going to be shared,
 * that we can clip, and that we should apply UPL optimizations */
#define VMRL_ERR_PREPARE_FOR_SHARE_WITH_UPL   err_vm_map_lock(6)
/*! denote from the lock that a try_lock failed because a lock was already held */
#define VMRL_ERR_LOCK_ALREADY_HELD      err_vm_map_lock(7)


/*
 * Explanation of the two types of submaps:
 * There are two types of submaps supported by the VM today.
 * For both types, nested submaps are disallowed, which means that a submap cannot
 * have an entry that is a submap itself.
 *
 * 1) constant submaps
 * Constant submaps are submaps that have static vm_map_entry_t's.
 * That means once "sealed", there is a guarantee that no vm_map_entry_t in that
 * submap will change in any way. This means no locks need to be held on constant submaps.
 * When descending into a constant submap, address transformation is required
 * (see vm_map_parent_address_to_submap_address).
 * Today's examples of these are the shared cache and the x86 commpage.
 *
 * 2) transparent submaps
 * Transparent submaps are submaps created by kmem_suballoc(). They are mapped
 * only in the kernel_map and use the kernel_pmap. Transparent submaps cannot be
 * mapped in multiple entries. They are guaranteed to have VME_OFFSET(entry) == entry->vme_start
 * for the submap entry, which means there is no address transformation required when descending
 * into them (e.g. address A in a parent map is also address A in the submap). That, combined
 * with the automatic descension by the VM into transparent submaps
 * justify the name "transparent" (they are transparent to clients and the VM).
 * An entry mapping a transparent submap must be atomic and permanent.
 * examples include the ipc_kernel_map submap and the compressor submap
 *
 * The range lock does not permit being called directly on a constant submap
 * once it has been sealed, as it does not lock constant submap entries.
 * Transparent submaps are allowed.
 */


#if MACH_ASSERT

/*
 * Debugging validation of a map when its interlock is locked or unlocked.
 * These checks exist in development builds only.
 * Fast checks (always on) go in the inline functions.
 * Slow checks (enabled by boot-arg) go in the extern functions.
 * We also check mach_assert_enabled() to allow static_if to optimize
 * these fast paths.
 */

extern bool vm_debug_any_options_enabled;

static inline OS_ALWAYS_INLINE void
vm_map_debug_after_lock_fast(vm_map_t map)
{
	if (mach_assert_enabled() && vm_debug_any_options_enabled) {
		(void)map;
	}
}

static inline OS_ALWAYS_INLINE void
vm_map_debug_before_unlock_fast(vm_map_t map)
{
	if (mach_assert_enabled() && vm_debug_any_options_enabled) {
		(void)map;
	}
}

#else  /* not MACH_ASSERT */

#define vm_map_debug_after_lock_fast(map) ({})
#define vm_map_debug_before_unlock_fast(map) ({})

#endif /* not MACH_ASSERT */

#ifndef VM_MAP_LOCK_PRIVATE

/*!
 * @function vm_map_ilk_lock()
 *
 * @brief
 * Takes the interlock of the specified map.
 *
 * @discussion
 * The interlock protects non-atomic variables on the vm_map_t such as
 * the rb-tree or map->size
 */
static inline void
vm_map_ilk_lock(vm_map_t map)
{
	assert(!vm_map_is_sealed(map));
	lck_rw_lock_exclusive(&map->ilock);
	vm_map_debug_after_lock_fast(map);
}

/*!
 * @function vm_map_ilk_unlock()
 *
 * @brief
 * Drops the interlock of the specified map.
 */
static inline void
vm_map_ilk_unlock(vm_map_t map)
{
	assert(!vm_map_is_sealed(map));
	vm_map_debug_before_unlock_fast(map);
	lck_rw_unlock_exclusive(&map->ilock);
}

/*!
 * @function vm_map_ilk_lock_shared()
 *
 * @brief
 * Takes the interlock of the specified map in shared mode.
 *
 * @discussion
 * The interlock protects non-atomic variables on the vm_map_t such as
 * the rb-tree or map->size
 */
static inline void
vm_map_ilk_lock_shared(vm_map_t map)
{
	assert(!vm_map_is_sealed(map));
	lck_rw_lock_shared(&map->ilock);
}

/*!
 * @function vm_map_ilk_unlock_shared()
 *
 * @brief
 * Drops the interlock of the specified map.
 */
static inline void
vm_map_ilk_unlock_shared(vm_map_t map)
{
	assert(!vm_map_is_sealed(map));
	lck_rw_unlock_shared(&map->ilock);
}

/*!
 * @function vm_map_ilk_lock_allow_sealed()
 *
 * @brief
 * Takes the interlock of the specified map, allowed on sealed maps.
 *
 * @discussion
 * This is the same as @c vm_map_ilk_lock(), but allows for sealed maps,
 * which is useful to handle sealing/unsealing transitions.
 */
static inline void
vm_map_ilk_lock_allow_sealed(vm_map_t map)
{
	lck_rw_lock_exclusive(&map->ilock);
	vm_map_debug_after_lock_fast(map);
}

/*!
 * @function vm_map_ilk_unlock_allow_sealed()
 *
 * @brief
 * Drops the interlock of the specified map, allowed on sealed maps.
 *
 * @discussion
 * This is the same as @c vm_map_ilk_unlock(), but allows for sealed maps,
 * which is useful to handle sealing/unsealing transitions.
 */
static inline void
vm_map_ilk_unlock_allow_sealed(vm_map_t map)
{
	vm_map_debug_before_unlock_fast(map);
	lck_rw_unlock_exclusive(&map->ilock);
}

/*!
 * @function vm_map_ilk_sleep()
 *
 * @brief
 * Sleep while releasing the map interlock, waiting for a wakeup on an event.
 * Upon return, the map interlock is held again.
 */
static inline wait_result_t
vm_map_ilk_sleep(vm_map_t map, event_t event, wait_interrupt_t interruptible)
{
	wait_result_t wr;
	vm_map_debug_before_unlock_fast(map);
	wr = lck_rw_sleep(&map->ilock, LCK_SLEEP_DEFAULT,
	    event, interruptible);
	vm_map_debug_after_lock_fast(map);
	return wr;
}

static inline void
assert_vm_map_ilk_owned(vm_map_t map, lck_rw_type_t how)
{
	(void) map;
	if (lck_rw_assert_enabled()) {
		lck_rw_assert_held_type(&map->ilock, how);
	}
}

static inline void
assert_vm_map_ilk_not_owned(vm_map_t map __assert_only)
{
	LCK_RW_ASSERT(&map->ilock, LCK_RW_ASSERT_NOT_OWNED);
}

#else /* !VM_MAP_LOCK_PRIVATE */

/* Internal errors to the vm map/entry locks */
#define VMRL_ERR_RELOOKUP               err_vm_map_lock(0x101)
#define VMRL_ERR_ABORTED                err_vm_map_lock(0x102)
#define VMRL_ERR_NOT_FOUND              err_vm_map_lock(0x103)

#endif /* !VM_MAP_LOCK_PRIVATE */

static inline void
assert_vm_map_ilk_owned_ignore_sealed(vm_map_t map, lck_rw_type_t how)
{
	if (!vm_map_is_sealed_or_will_be_sealed(map) && lck_rw_assert_enabled()) {
		lck_rw_assert_held_type(&map->ilock, how);
	}
}

/*!
 * @function vm_map_has_entry_at_address_ilocked()
 *
 * @brief
 * Checks whether an entry is present in the provided map at the provided
 * address. Should be called with the interlock held, returns with the
 * interlock held. Doesn't drop the interlock.
 */
static inline bool
vm_map_has_entry_at_address_ilocked(
	vm_map_t                map,
	vm_map_offset_t         addr)
{
	return vm_map_lookup(map, addr) != VM_MAP_ENTRY_NULL;
}

/*!
 * @typedef vmrl_flags_t
 *
 * @brief
 * Flags affecting the behavior of acquiring a range lock.
 *
 *
 * <h1>locking mode, exactly one must be selected</h1>
 *
 * @const VMRL_STREAM (ex, sh)
 * For cases where we want to allow holes (for example: vm_map_inherit(),
 * vm_map_delete() when used for termination, and most uses of shared locking),
 * make the lock stream rather than lock the whole range. This means
 * the lock's behavior is not atomic and instead the next entry is locked
 * on advancing entries.
 *
 * Note that when streaming there is no guarantee that entries being returned
 * move "forward" without overlaps as concurrent mutations of the map might
 * cause coalescing concurrently. As a result it is important to query the
 * bounds to apply to the returned entry in code handling entries using
 * @c vm_map_lock_ctx_bounds() or similar.
 *
 * @const VMRL_STREAM_NO_HOLES (ex, sh)
 * Similar to VMRL_STREAM, however, when a hole has been detected,
 * the iteration will fail with KERN_INVALID_ADDRESS.
 *
 * @const VMRL_ATOMIC (ex, sh)
 * Denotes an atomic (i.e. non-streaming) lock. The whole range will be held
 * locked for the entire duration of the operation (from lock to unlock) and
 * holes will not be allowed (attempting to lock a range with holes will return
 * an error).
 *
 * @const VMRL_ATOMIC_ALLOW_HOLES (ex)
 * Similar to @c VMRL_ATOMIC but allows for holes by inserting sentinel entries
 * to denote locked holes.
 *
 * They will be returned during iteration as placeholders that clients must
 * ignore, however.
 *
 * This should only be used to implement fixed overwrite mappings in the context
 * of enter/delete. Other APIs should stream or use VMRL_ATOMIC. This is because
 * sentinel entries take up space that is not available for other allocations,
 * which could potentially cause them to fail spuriously.
 *
 *
 * <h1>shared/exclusive locking, exactly one must be selected</h1>
 *
 * Note that this flag is selected implicitly via the usage of @c VMRL_EX_*
 * versus @c VMRL_SH_* namespaced flags.
 *
 * @const VMRL_SHARED (implicit: sh)
 * Denotes a shared lock.
 *
 * @const VMRL_EXCLUSIVE (implicit: ex)
 * Denotes an exclusive lock.
 *
 *
 * <h1>Entry preparation<h1>
 *
 * @const VMRL_VMO_ALLOCATE (sh)
 * Allocate a vm object if VME_OBJECT(entry) == NULL. Do not resolve CoW.
 *
 * @const VMRL_RESOLVE_COW_AND_OBJ
 * Resolve the vm_object or submap of each entry, which means for
 * 1) entries with a NULL object: allocate one.
 * 2) entries with needs_copy=true: resolve object level or submap level CoW.
 *
 * Clients of the range lock will be given an entry with needs_copy=false and
 * an object or submap associated.
 * This will do pmap unnesting if a submap needs to be resolved. For resolving
 * submap CoW, it essentially copies the submap entries into the top level map
 * and creates a shadow object.
 * For object CoW, it just creates a shadow object.
 *
 * @const VMRL_NO_PMAP_UNNEST (ex)
 * Do not unecessarily pmap unnest any submap entries within the range lock.
 * Those entries will still be pmap_unnested if a clip would need to be performed.
 *
 * <h1>Behavior modifiers<h1>
 *
 * @const VMRL_NO_MIN_MAX_CHECK (ex, sh)
 * Allow the range to go beyond the map [min_offset, max_offset).
 * Without this flag, locks will fail with KERN_INVALID_ADDRESS
 * if the requested range goes beyond this range.
 * This should not be used with VMRL_WHOLE_MAP since they mean similar things.
 *
 * @const VMRL_WHOLE_MAP (ex, sh)
 * Lock the entire virtual address range, including entries beyond
 * [min_offset, max_offset).
 * start and end of the lock must be set to VMRL_WHOLE_MAP_START, VMRL_WHOLE_MAP_END
 * This should not be used with VMRL_NO_MIN_MAX_CHECK since they mean similar things.
 *
 * @const VMRL_SIMPLIFY (ex)
 * Whether entry coalescing/simplification should be attempted.
 * This happens at unlock time for ATOMIC locks and advance or drop time for
 * STREAM locks.
 *
 * @const VMRL_INTERRUPTIBLE (ex, sh)
 * Whether waiting for a lock is interruptible. When interrupted,
 * the lock acquiring or enumeration will fail with KERN_ABORTED.
 *
 * @const VMRL_ILK_LOCKED (ex, sh)
 * Whether the interlock of the map is held at the time
 * of lock acquisition, functions will always return with
 * it unlocked.
 *
 * @const VMRL_TRY_LOCK_ENTRY (ex,sh)
 * Do a vm_entry_try_lock_* on the entry rather than a sleepable lock. If the entry
 * lock being acquired is already held (the trylock failed), VMRL_ERR_LOCK_ALREADY_HELD
 * is returned instead.
 *
 * @const VMRL_SH_NO_DESCEND_TRANSPARENT (sh streaming only)
 * Do not descend into transparent submaps. This may be useful for APIs
 * specifically wanting to examine the structure of the address space in the
 * kernel_map.
 *
 * @const VMRL_DESCEND_INTO_CONSTANT (ex, sh)
 * Whether iteration descends into constant submaps.
 * Descension into transparent submaps happens by default.
 * Exclusive locks:
 *   - Can be made to descend into constant submap by passing
 *     VMRL_EX_DESCEND_INTO_CONSTANT. Even though this an exclusive lock, there
 *     are actually no locks taken in constant submaps. When descended into a
 *     constant submap, the caller must not modify entries.
 * Shared locks:
 *   - Can be made to descend into constant submaps by passing
 *     VMRL_SH_DESCEND_INTO_CONSTANT
 */
__options_decl(vmrl_flags_t, uint32_t, {
	/* enumeration mode */
	_VMRL_NO_HOLES                = 0x00000001,
	_VMRL_ALLOW_HOLES             = 0x00000002,
	_VMRL_STREAM_INTERNAL         = 0x00000004,
	_VMRL_ATOMIC_INTERNAL         = 0x00000008,
	_VMRL_MODE_MASK               = 0x0000000f,

	VMRL_INVALID                  = 0x00000000,
	VMRL_STREAM                   = _VMRL_STREAM_INTERNAL | _VMRL_ALLOW_HOLES,
	VMRL_STREAM_NO_HOLES          = _VMRL_STREAM_INTERNAL | _VMRL_NO_HOLES,
	VMRL_ATOMIC                   = _VMRL_ATOMIC_INTERNAL | _VMRL_NO_HOLES,
	VMRL_ATOMIC_ALLOW_HOLES       = _VMRL_ATOMIC_INTERNAL | _VMRL_ALLOW_HOLES,

	/* exclusive / shared */
	VMRL_SHARED                   = 0x00000010,
	VMRL_EXCLUSIVE                = 0x00000020,

	/* entry stabilization */
	VMRL_RESOLVE_COW_AND_OBJ      = 0x00000040,
	VMRL_VMO_ALLOCATE             = 0x00000080,
	VMRL_NO_PMAP_UNNEST           = 0x00000100,

	/* other flags */
	VMRL_WHOLE_MAP                = 0x00001000,
	VMRL_NO_MIN_MAX_CHECK         = 0x00002000,
	VMRL_SIMPLIFY                 = 0x00004000,
	VMRL_INTERRUPTIBLE            = 0x00008000,
	VMRL_ILK_LOCKED               = 0x00010000,
	VMRL_TRY_LOCK_ENTRY           = 0x00020000,
	VMRL_NO_DESCEND_TRANSPARENT   = 0x00040000,
	VMRL_DESCEND_INTO_CONSTANT    = 0x00080000,

	/*
	 * internal flags set by the implementation only
	 * for the duration of a function call and never persisted.
	 */
	_VMRL_SETUP_COW                = 0x01000000,
	_VMRL_SETUP_COW_NOCLIP         = 0x02000000,
	_VMRL_PREPARE_FOR_SHARE_NOCLIP = 0x04000000,
	_VMRL_PREPARE_FOR_SHARE_WITH_UPL = 0x08000000,

	/* internal flags set by the implementation only */
	_VMRL_SINGLE_ENTRY            = 0x40000000,
	_VMRL_KERNEL_PMAP             = 0x80000000,
});

/*!
 * @typedef vmrl_ex_flags_t
 *
 * @brief
 * Flags affecting the behavior of acquiring an exclusive range lock.
 *
 * @discussion
 * The minimal behavior for the exclusive range lock is to:
 * - clip entries within the [start, end) range being asked,
 * - pmap unnest said entries if necessary.
 */
__options_decl(vmrl_ex_flags_t, uint32_t, {
	/* enumeration mode */
	VMRL_EX_STREAM                = VMRL_EXCLUSIVE | VMRL_STREAM,
	VMRL_EX_STREAM_NO_HOLES       = VMRL_EXCLUSIVE | VMRL_STREAM_NO_HOLES,
	VMRL_EX_ATOMIC                = VMRL_EXCLUSIVE | VMRL_ATOMIC,
	VMRL_EX_ATOMIC_ALLOW_HOLES    = VMRL_EXCLUSIVE | VMRL_ATOMIC_ALLOW_HOLES,

	/* entry stabilization */
	VMRL_EX_RESOLVE_COW_AND_OBJ   = VMRL_RESOLVE_COW_AND_OBJ,
	VMRL_EX_NO_PMAP_UNNEST        = VMRL_NO_PMAP_UNNEST,

	/* other flags */
	VMRL_EX_WHOLE_MAP             = VMRL_WHOLE_MAP,
	VMRL_EX_NO_MIN_MAX_CHECK      = VMRL_NO_MIN_MAX_CHECK,
	VMRL_EX_SIMPLIFY              = VMRL_SIMPLIFY,
	VMRL_EX_INTERRUPTIBLE         = VMRL_INTERRUPTIBLE,
	VMRL_EX_ILK_LOCKED            = VMRL_ILK_LOCKED,
	VMRL_EX_TRY_LOCK_ENTRY        = VMRL_TRY_LOCK_ENTRY,
	VMRL_EX_DESCEND_INTO_CONSTANT = VMRL_DESCEND_INTO_CONSTANT,
});

/*!
 * @typedef vmrl_sh_flags_t
 *
 * @brief
 * Flags affecting the behavior of acquiring a shared range lock.
 *
 * @discussion
 * The default behavior for the shared range lock is to be streaming,
 * (however that isn't true for a downgraded exclusive lock).
 */
__options_decl(vmrl_sh_flags_t, uint32_t, {
	/* enumeration mode */
	VMRL_SH_STREAM                 = VMRL_SHARED | VMRL_STREAM,
	VMRL_SH_STREAM_NO_HOLES        = VMRL_SHARED | VMRL_STREAM_NO_HOLES,
	VMRL_SH_ATOMIC                 = VMRL_SHARED | VMRL_ATOMIC,

	/* entry stabilization */
	VMRL_SH_RESOLVE_COW_AND_OBJ    = VMRL_RESOLVE_COW_AND_OBJ,
	VMRL_SH_VMO_ALLOCATE           = VMRL_VMO_ALLOCATE,

	/* other flags */
	VMRL_SH_WHOLE_MAP              = VMRL_WHOLE_MAP,
	VMRL_SH_NO_MIN_MAX_CHECK       = VMRL_NO_MIN_MAX_CHECK,
	VMRL_SH_INTERRUPTIBLE          = VMRL_INTERRUPTIBLE,
	VMRL_SH_ILK_LOCKED             = VMRL_ILK_LOCKED,
	VMRL_SH_TRY_LOCK_ENTRY         = VMRL_TRY_LOCK_ENTRY,
	VMRL_SH_NO_DESCEND_TRANSPARENT = VMRL_NO_DESCEND_TRANSPARENT,
	VMRL_SH_DESCEND_INTO_CONSTANT  = VMRL_DESCEND_INTO_CONSTANT,
});

/*!
 * @typedef vmrl_find_ex_flags_t
 *
 * @brief
 * Flags affecting the behavior of acquiring an exclusive lock on an entry at or
 * after an address.
 */
__options_decl(vmrl_find_ex_flags_t, uint32_t, {
	VMRL_FIND_EX_DEFAULT          = 0,

	/* entry stabilization */
	VMRL_FIND_EX_RESOLVE_COW_AND_OBJ = VMRL_RESOLVE_COW_AND_OBJ,

	/* other flags */
	VMRL_FIND_EX_NO_MIN_MAX_CHECK = VMRL_NO_MIN_MAX_CHECK,
	VMRL_FIND_EX_INTERRUPTIBLE    = VMRL_INTERRUPTIBLE,
	VMRL_FIND_EX_ILK_LOCKED       = VMRL_ILK_LOCKED,
});

/*!
 * @typedef vmrl_find_sh_flags_t
 *
 * @brief
 * Flags affecting the behavior of acquiring a shared lock on an entry at or
 * after an address.
 */
__options_decl(vmrl_find_sh_flags_t, uint32_t, {
	VMRL_FIND_SH_DEFAULT          = 0,

	/* entry stabilization */
	VMRL_FIND_SH_RESOLVE_COW_AND_OBJ = VMRL_RESOLVE_COW_AND_OBJ,
	VMRL_FIND_SH_VMO_ALLOCATE        = VMRL_VMO_ALLOCATE,

	/* other flags */
	VMRL_FIND_SH_NO_MIN_MAX_CHECK    = VMRL_NO_MIN_MAX_CHECK,
	VMRL_FIND_SH_INTERRUPTIBLE       = VMRL_INTERRUPTIBLE,
	VMRL_FIND_SH_ILK_LOCKED          = VMRL_ILK_LOCKED,
	VMRL_FIND_SH_NO_DESCEND_TRANSPARENT = VMRL_NO_DESCEND_TRANSPARENT,
	VMRL_FIND_SH_DESCEND_INTO_CONSTANT = VMRL_DESCEND_INTO_CONSTANT,
});

/*!
 * @brief
 * The state of descent in submaps.
 */
__enum_decl(vmlc_descend_t, unsigned char, {
	VMLC_NOT_DESCENDED,
	VMLC_IN_TRANSPARENT_SUBMAP,
	VMLC_IN_CONSTANT_SUBMAP,
});

/*!
 * @brief
 * These values should be used in conjunction with VMRL_X_WHOLE_MAP.
 * Their purpose is to alleviate the need from the user to specify the
 * exact limits of the first and last virtual addresses.
 */
#define VMRL_WHOLE_MAP_START (0)
#define VMRL_WHOLE_MAP_END (0)

/* The values are invalid VAs which are meant to fail the checks in
 * __vmrl_context_init() if left unmodified.  */
static_assert(VMRL_WHOLE_MAP_START == VMRL_WHOLE_MAP_END);

/*!
 * @brief
 * These values can be used in conjunction with VMRL_X_NO_OFFSET_CHECK
 * to iterate the map range from the start of the virtual address space
 * or to the end of the virtual address space.
 */
#define VMRL_START_VA(map) (0)
#define VMRL_END_VA(map)  ((vm_map_address_t)(-vm_map_page_size(map)))

typedef struct vm_map_lock_ctx *vm_map_lock_ctx_t;
typedef vm_map_lock_ctx_t vm_map_find_lock_ctx_t;

typedef kern_return_t (^vm_map_lock_preflight_t)(vm_map_lock_ctx_t ctx, vm_map_entry_t entry);

struct vm_map_lock_ctx {
	/*
	 * Denotes the clip that must be applied to the entry range
	 * relative to the current map.
	 * In stream lock vmlc_req_start is updated on every iteration to the current start address
	 */
	mach_vm_address_t       vmlc_req_start;
	mach_vm_address_t       vmlc_req_end;

/* public fields */

	/*!
	 * The map the current entry belongs to,
	 * this might differ from the original map
	 * passed to __vm_map_range_*_lock().
	 */
	vm_map_t                vmlc_map;

	/*! cursor state: the current vm entry */
	vm_map_entry_t          vmlc_vme;

	vm_map_lock_preflight_t vmlc_preflight;

/* private fields */

	/*! the flags passed to the __vm_map_range_*_lock() call */
	vmrl_flags_t            __vmlc_flags;

	/*! is a lock actively held */
	bool                    __vmlc_locked   : 1;
	/*! is this the first iteration */
	bool                    __vmlc_first    : 1;

	/*! Is the lock currently in a submap */
	vmlc_descend_t          __vmlc_descended : 2;

	uint32_t                __vmlc_unused1  : 28;

	union {
		/*! This structure is used for VMRL_STREAM contexts */
		struct {
			/*
			 * The last address that's been processed (or the start
			 * of the range, if we've just started)
			 */
			mach_vm_address_t       last_processed_addr;
			kern_return_t           first_error;
			uint32_t                __vmlc_unused2;
		} __vmlc_streaming;

		/*! This structure is used for VMRL_ATOMIC contexts */
		struct {
			vm_map_entry_t          first_entry;
			mach_vm_address_t       locked_range_start;
			mach_vm_address_t       locked_range_end;
		} __vmlc_atomic;
	};


	/*
	 * If we are descended in a submap, store
	 * start/end/map of the parent map
	 */
	vm_map_address_t        __original_req_start;
	vm_map_address_t        __original_req_end;
	vm_map_t                __original_map;
	vm_map_offset_t         __parent_offset; /* 0 if not descended */
	vm_map_entry_t          __parent_entry;
};


#pragma mark flags acessors

__attribute__((overloadable, always_inline, const))
static inline vmrl_flags_t
__vmrl_flags(vmrl_flags_t flags)
{
	return flags;
}

__attribute__((overloadable, always_inline))
static inline vmrl_flags_t
__vmrl_flags(vm_map_lock_ctx_t ctx)
{
	return ctx->__vmlc_flags;
}

__attribute__((overloadable, always_inline, const))
static inline vmrl_flags_t
__vmrl_flags(vmrl_sh_flags_t flags)
{
	return (vmrl_flags_t)flags;
}

__attribute__((overloadable, always_inline, const))
static inline vmrl_flags_t
__vmrl_flags(vmrl_ex_flags_t flags)
{
	return (vmrl_flags_t)flags;
}

__attribute__((overloadable, always_inline, const))
static inline vmrl_flags_t
__vmrl_flags(vmrl_find_sh_flags_t flags)
{
	return (vmrl_flags_t)flags;
}

__attribute__((overloadable, always_inline, const))
static inline vmrl_flags_t
__vmrl_flags(vmrl_find_ex_flags_t flags)
{
	return (vmrl_flags_t)flags;
}

/*!
 * @brief
 * Returns whether the specified flags or context denote a shared lock.
 */
#define vmrl_is_shared(x) \
	((bool)((__vmrl_flags(x) & (VMRL_SHARED | VMRL_EXCLUSIVE)) == VMRL_SHARED))


/*!
 * @brief
 * Returns whether the specified flags or context denote an exclusive lock.
 */
#define vmrl_is_exclusive(x) \
	((bool)((__vmrl_flags(x) & (VMRL_SHARED | VMRL_EXCLUSIVE)) == VMRL_EXCLUSIVE))

/*!
 * @brief
 * Denotes whether the specified context is for a kernel map.
 */
#define vmrl_is_kernel_pmap(x) \
	((bool)(__vmrl_flags(x) & _VMRL_KERNEL_PMAP))


/*!
 * @brief
 * Returns the mode of the lock (invalid, streaming, stream-no-holes,
 * atomic, or atomic-allow-holes).
 */
#define vmrl_mode(x) \
	(__vmrl_flags(x) & _VMRL_MODE_MASK)


/*!
 * @brief
 * Returns whether the specified flags or context denote a lock in streaming
 * mode.
 */
#define vmrl_is_streaming(x) \
	((bool)(vmrl_mode(x) & (_VMRL_STREAM_INTERNAL)))

/*!
 * @brief
 * Returns whether the specified flags or context denote a lock in atomic
 * mode.
 */
#define vmrl_is_atomic(x) \
	((bool)(vmrl_mode(x) & (_VMRL_ATOMIC_INTERNAL)))

/*!
 * @brief
 * Computes the wait flags (interruptible or not) for the specified flags or
 * context.
 */
#define vmrl_wait_interrupt(x) \
	((__vmrl_flags(x) & VMRL_INTERRUPTIBLE) ? THREAD_ABORTSAFE : THREAD_UNINT)

#pragma mark lock context

/*!
 * @brief
 * Static initializer for map range lock contexts.
 */
#define VM_MAP_LOCK_CTX_INITIALIZER  { }

/*
 * @function vm_map_lock_ctx_init()
 *
 * @brief
 * Initialize a map lock context to an empty state ready to be used for locking.
 */
static inline void
vm_map_lock_ctx_init(vm_map_lock_ctx_t vml_ctx)
{
	*vml_ctx = (struct vm_map_lock_ctx)VM_MAP_LOCK_CTX_INITIALIZER;
}

/*
 * @function assert_vm_map_lock_ctx_unlocked()
 *
 * @brief
 * Helper function to assert that a lock is unlocked.
 *
 * @discussion
 * This should not be called directly,
 * and is called internally by VM_MAP_LOCK_CTX_DECLARE.
 */
static inline void
assert_vm_map_lock_ctx_unlocked(vm_map_lock_ctx_t vml_ctx __assert_only)
{
	assert(!vml_ctx->__vmlc_locked);
}

/*!
 * @brief
 * Declare a lock context called @c name on the stack.
 *
 * @discussion
 * The context can be used for serveral lock acquisition in a row,
 * but the lock must be unlocked on any exit path from the function.
 */
#define VM_MAP_LOCK_CTX_DECLARE(name) \
	__attribute__((cleanup(assert_vm_map_lock_ctx_unlocked))) \
	struct vm_map_lock_ctx private_##name = VM_MAP_LOCK_CTX_INITIALIZER; \
	const vm_map_lock_ctx_t name = &private_##name

/*!
 * @brief
 * Declare a lock context called @c name on the stack for finding a single entry
 * at (or after) an address
 */
#define VM_MAP_FIND_LOCK_CTX_DECLARE(name) \
	__attribute__((cleanup(assert_vm_map_lock_ctx_unlocked))) \
	struct vm_map_lock_ctx private_##name = VM_MAP_LOCK_CTX_INITIALIZER; \
	const vm_map_find_lock_ctx_t name = &private_##name

/*!
 * @brief
 * Sets up the preflight hook in a context.
 *
 * @discussion
 * The preflight hook allows for lockers to add custom behaviors and validation
 * as the lock is being taken.
 *
 * For atomic locks, the preflight outcome will be returned by
 * @c vm_map_range_*_lock(), for streaming locks it is returned via the error
 * parameter of the @c vm_map_range_next_with_error() family of functions.
 * For atomic locks, the preflight is called on every entry during the process
 * of locking the range.
 * For streaming locks, the preflight is called on each entry at advance time.
 *
 * In all cases (incl. transparent submaps or constant submaps with
 * VMRL_DESCEND_INTO_CONSTANT), the preflight hook gets called on any entry that
 * is being iterated.
 *
 * The preflight hook may be called multiple times on the same entry.
 *
 * Preflight hooks are called with the entry locked exclusively for exclusive
 * locks, and at least shared for shared locks.
 *
 * Preflights are run with the interlock held. The intent is for them to make
 * policy checks by reading fields of the entry, or fields of the associated
 * object that are safe to access without the object lock. They should not
 * modify the state of the VM. They should ideally run quickly and not perform
 * any blocking operations such as acquiring an object lock.
 *
 * The preflight hook can return the following values:
 *
 * - KERN_SUCCESS       The entry is accepted.
 *
 * - VMRL_ERR_SKIP_PREPARE
 *                      The entry should not be "prepared" (no unnesting,
 *                      no clipping, etc...).
 *
 *                      This error doesn't stop enumeration and is swallowed,
 *                      and during the enumeration, it is the responsibility
 *                      of the client to notice this entry has not been
 *                      prepared and shouldn't be mutated.
 *
 *                      This error is processed internally by the lock and will
 *                      not be returned to clients of the @c vm_map_range_*_lock()
 *                      or @c vm_map_range_next_with_error() APIs.
 *
 * - VMRL_ERR_WAIT_FOR_KUNWIRE
 *                      The caller desires waiting for the wire count of the
 *                      entry to drop.
 *
 *                      This error doesn't stop enumeration, but the preflight
 *                      hook will be called on this entry again once the
 *                      kernel wire count condition changed. It will also cause
 *                      @c vm_map_range_*_lock() or @c vm_map_range_next_with_error()
 *                      to hang until the wire count drops.
 *
 *                      This error is processed internally by the lock and will
 *                      not be returned to clients of the @c vm_map_range_*_lock()
 *                      or @c vm_map_range_next_with_error() APIs.
 *
 * - VMRL_ERR_SETUP_SYMMETRIC_COW
 *                      The caller requests to setup COW on the entry.
 *                      If the lock is a shared lock, this will temporarily
 *                      take an exclusive lock on the entry.
 *
 * - VMRL_ERR_SETUP_SYMMETRIC_COW_NOCLIP
 *                      Same as VMRL_ERR_SETUP_SYMMETRIC_COW but entries are not clipped
 *                      to the requested range.
 *                      This serves to preserve the behaviour of APIs that have not
 *                      clipped before the range-lock change.
 *                      clipping behaviour is an internal implementation detail, except
 *                      in the case of mach_make_memory_entry() where the effects of
 *                      clipping is exposed to the user due to use of
 *                      vmk_flags.vmkf_copy_single_object. These two _NOCLIP flags
 *                      cater to this case.
 *
 * - VMRL_ERR_PREPARE_FOR_SHARE_NOCLIP
 *                      The caller indicates that the entry is going to be shared
 *                      with another map, but that they do not want the entry to
 *                      be clipped. The lock stabilizes the object for the entry
 *                      and sets is_shared accordingly.
 *                      If the entry is locked shared it will temporarily upgrade to exclusive
 *                      Entries are not clipped to the requested range, see above comment.
 *
 * - VMRL_ERR_PREPARE_FOR_SHARE_WITH_UPL
 *                      The caller indicates that the entry is going to be shared,
 *                      and that it may be clipped.
 *                      The lock stabilizes the object for the entry but does not
 *                      set is_shared.
 *                      If the entry is locked shared it will temporarily upgrade to exclusive
 *
 * - other              An error that stops the enumeration and will be bubbled
 *                      up to callers.
 */
static inline void
vm_map_lock_ctx_set_preflight(
	vm_map_lock_ctx_t       vml_ctx,
	vm_map_lock_preflight_t preflight)
{
	assert_vm_map_lock_ctx_unlocked(vml_ctx);
	vml_ctx->vmlc_preflight = preflight;
}

/*!
 * @brief
 * Converts a parent map address to be relative to the currently
 * iterated map (possibly a submap).
 */
static inline vm_map_offset_t
vm_map_lock_ctx_from_parent_address(vm_map_lock_ctx_t ctx, vm_map_address_t addr)
{
	return addr - ctx->__parent_offset;
}

/*!
 * @brief
 * Converts an address relative to the currently iterated map to be relative
 * to the parent map.
 */
static inline vm_map_offset_t
vm_map_lock_ctx_to_parent_address(vm_map_lock_ctx_t ctx, vm_map_address_t addr)
{
	return addr + ctx->__parent_offset;
}

/*!
 * @brief
 * Returns the entry offset for a given address, relative to the currently
 * iterated map (possibly a submap).
 */
static inline vm_map_offset_t
vm_map_lock_ctx_offset_for_address(vm_map_lock_ctx_t ctx, vm_map_address_t addr)
{
	vm_map_entry_t vme = ctx->vmlc_vme;

	/*
	 * the address really should be within the entry,
	 * and within the enumeration range.
	 * (<= because they may want to know about the end addr)
	 */
	assert(ctx->vmlc_req_start <= addr && addr <= ctx->vmlc_req_end);
	assert(vme->vme_start <= addr && addr <= vme->vme_end);

	return VME_OFFSET(vme) + addr - vme->vme_start;
}


/*!
 * @brief
 * Returns the entry offset for a given address relative to the parent map
 * that is currerntly iterated.
 */
static inline vm_map_offset_t
vm_map_lock_ctx_offset_for_parent_address(
	vm_map_lock_ctx_t       ctx,
	vm_map_address_t        addr)
{
	addr = vm_map_lock_ctx_from_parent_address(ctx, addr);
	return vm_map_lock_ctx_offset_for_address(ctx, addr);
}


/*!
 * @brief
 * Return the bounds of the current entry, relative to the currently
 * iterated map (possibly a submap).
 *
 * @discussion
 * This function will take the requested bounds into account,
 * and present the real bounds of the entry that the caller should consider.
 *
 * @param startp        (optional out parameter) the entry start.
 * @param endp          (optional out parameter) the entry end.
 * @param sizep         (optional out parameter) the entry size.
 */
static inline void
vm_map_lock_ctx_bounds(
	vm_map_lock_ctx_t       ctx,
	vm_map_address_t       *startp,
	vm_map_address_t       *endp,
	vm_map_size_t          *sizep)
{
	vm_map_entry_t   vme   = ctx->vmlc_vme;
	vm_map_address_t start = MAX(vme->vme_start, ctx->vmlc_req_start);
	vm_map_address_t end   = MIN(vme->vme_end, ctx->vmlc_req_end);

	if (startp) {
		*startp = start;
	}
	if (endp) {
		*endp = end;
	}
	if (sizep) {
		*sizep = end - start;
	}
}

/*!
 * @brief
 * Return the bounds of the current entry, relative to the currently
 * iterated parent map, regardless of whether the iteration descended
 * into a submap.
 *
 * @discussion
 * This function will take the requested bounds into account,
 * and present the real bounds of the entry that the caller should consider.
 *
 * @param startp        (optional out parameter) the entry start.
 * @param endp          (optional out parameter) the entry end.
 * @param sizep         (optional out parameter) the entry size.
 */
static inline void
vm_map_lock_ctx_bounds_in_parent(
	vm_map_lock_ctx_t       ctx,
	vm_map_address_t       *startp,
	vm_map_address_t       *endp,
	vm_map_size_t          *sizep)
{
	vm_map_lock_ctx_bounds(ctx, startp, endp, sizep);
	if (startp) {
		*startp = vm_map_lock_ctx_to_parent_address(ctx, *startp);
	}
	if (endp) {
		*endp = vm_map_lock_ctx_to_parent_address(ctx, *endp);
	}
}

/*!
 * @brief
 * Return the offsets bounds of the current entry's object or submap.
 *
 * @discussion
 * This function will take the requested bounds into account,
 * and present the real offsets bounds of the object or submap
 * that the caller should consider.
 *
 * @param startp        (optional out parameter) the object/submap start.
 * @param endp          (optional out parameter) the object/submap end.
 * @param sizep         (optional out parameter) the object/submap size.
 */
static inline void
vm_map_lock_ctx_offset_bounds(
	vm_map_lock_ctx_t       ctx,
	vm_object_offset_t     *startp,
	vm_object_offset_t     *endp,
	vm_object_size_t       *sizep)
{
	vm_map_lock_ctx_bounds(ctx, startp, endp, sizep);
	if (startp) {
		*startp = vm_map_lock_ctx_offset_for_address(ctx, *startp);
	}
	if (endp) {
		*endp = vm_map_lock_ctx_offset_for_address(ctx, *endp);
	}
}


#pragma mark lock / unlock

/*!
 * @function vm_map_range_ex_lock()
 *
 * @discussion
 * This function acquires an exclusive range lock for the [start, end) range
 * within the @c map VM map.
 *
 * This function initiates the context cursor to the first entry
 * in the range.
 *
 * Exclusive locks descend into transparent submaps by default and do not
 * descend into constant submaps.
 *
 * @param vml_ctx       the locking context to use.
 * @param map           the map being locked.
 *                      This parameter will be NULLed on success to prevent it
 *                      accidentally being used incorrectly by client code, which
 *                      should instead use vm_map_lock_ctx_get_map()
 * @param start         the beginning of the range to lock
 * @param end           the end of the range to lock
 * @param flags         a set of flags altering the lock behavior
 *
 * @returns
 * - KERN_SUCCESS       the lock was acquired
 *
 * - KERN_ABORTED       the lock wasn't acquired because of the sleep operation
 *                      being interrupted (VMRL_SH_ATOMIC & VMRL_EX_INTERRUPTIBLE only).
 *
 * - KERN_INVALID_ADDRESS
 *                      the lock wasn't acquired because no entry was found
 *                      within [start, end) (impossible for
 *                      VMRL_EX_ATOMIC_ALLOW_HOLES).
 *
 * - KERN_INVALID_ADDRESS
 *                      the lock wasn't acquired because there was a gap within
 *                      [start, end) (only for VMRL_EX_ATOMIC or
 *                      VMRL_EX_STREAM_NO_HOLES).
 *
 * - VMRL_ERR_LOCK_ALREADY_HELD
 *                      the lock wasn't acquired because the entry was already
 *                      locked. (VMRL_SH_ATOMIC & VMRL_EX_TRY_LOCK_ENTRY only)
 *
 * - other              the lock wasn't acquired because preparing the entry
 *                      failed with that error (entry stabilization only).
 *
 * - any error          the lock wasn't acquired because its preflight
 *                      rejected it (vmlc_preflight being set and
 *                      VMRL_EX_ATOMIC only).
 */
__result_use_check
extern kern_return_t vm_map_range_ex_lock(
	vm_map_lock_ctx_t       vml_ctx,
	vm_map_t               *map,
	vm_map_address_t        start,
	vm_map_address_t        end,
	vmrl_ex_flags_t         flags)
__attribute__((__diagnose_if__((flags & _VMRL_MODE_MASK) == VMRL_INVALID,
    "no mode selected", "error")));

/*!
 * @function vm_map_range_sh_lock()
 *
 * @discussion
 * This function acquires a shared range lock for the [start, end) range
 * within the @c map VM map.
 *
 * This function initiates the context cursor to the first entry
 * in the range.
 *
 * @param vml_ctx       the locking context to use.
 * @param map           the map being locked.
 *                      This parameter will be NULLed on success to prevent it
 *                      accidentally being used incorrectly by client code, which
 *                      should instead use vm_map_lock_ctx_get_map()
 * @param start         the beginning of the range to lock
 * @param end           the end of the range to lock
 * @param flags         a set of flags altering the lock behavior
 *                      (VMRL_STREAM is implied)
 *
 * @returns
 * - KERN_SUCCESS       the lock was acquired
 *
 * - KERN_ABORTED       the lock wasn't acquired because of the sleep operation
 *                      being interrupted (VMRL_SH_ATOMIC & VMRL_SH_INTERRUPTIBLE only).
 *
 * - KERN_INVALID_ADDRESS
 *                      the lock wasn't acquired because no entry was found
 *                      within [start, end).
 *
 * - KERN_INVALID_ADDRESS
 *                      the lock wasn't acquired because there was a gap within
 *                      [start, end) (only for VMRL_SH_ATOMIC or
 *                      VMRL_SH_STREAM_NO_HOLES).
 *
 * - VMRL_ERR_LOCK_ALREADY_HELD
 *                      the lock wasn't acquired because the entry was already
 *                      locked. (VMRL_SH_ATOMIC & VMRL_SH_TRY_LOCK_ENTRY only)
 *
 * - other              the lock wasn't acquired because preparing the entry
 *                      failed with that error (entry stabilization only).
 *
 * - other              the lock wasn't acquired because its preflight
 *                      rejected it (vmlc_preflight being set only).
 */
__result_use_check
extern kern_return_t vm_map_range_sh_lock(
	vm_map_lock_ctx_t       vml_ctx,
	vm_map_t               *map,
	vm_map_address_t        start,
	vm_map_address_t        end,
	vmrl_sh_flags_t         flags)
__attribute__((__diagnose_if__((flags & _VMRL_MODE_MASK) == VMRL_INVALID,
    "no mode selected", "error")));

/*!
 * @function vm_map_range_ex_to_sh()
 *
 * @brief
 * Downgrades an atomic exclusive held range lock
 * to an atomic shared range lock.
 *
 * @discussion
 * Exclusive range locks can only be downgraded if:
 * - VMRL_EX_STREAM wasn't passed to @c vm_map_range_ex_lock().
 *
 * A downgrade will always happen atomically, meaning that it is
 * guaranteed that no other exclusive locks can come in between
 * the exclusive to shared transition
 *
 * Once downgraded, the shared range lock is _not_ in streaming
 * mode. This nuance is hidden behind the cursor/iterator API.
 *
 * Downgrades should only happen after entirely iterating an exclusive range.
 * They reset the cursor to the beginning of that range.
 *
 * @param vml_ctx       the context passed to @c vm_map_range_ex_lock()
 */
extern void vm_map_range_ex_to_sh(
	vm_map_lock_ctx_t       vml_ctx);

/*!
 * @function vm_map_lock_ctx_from_locked_entries()
 *
 * @brief
 * Configures a context to manage a series of already-exclusive-locked entries.
 *
 * @discussion
 * Once configured, the lock context is indistinguishable from the hypothetical
 * context that @c vm_map_range_ex_lock() would have configured had it been
 * used to atomically lock the same range.
 *
 * @param vml_ctx       the context to be initialized
 * @param map           the map containing the locked entries
 * @param start         the first address in the already-exclusive-locked range
 * @param size          the total size of the range mapped by the locked entries
 */
extern kern_return_t vm_map_lock_ctx_from_locked_entries(
	vm_map_lock_ctx_t   vml_ctx,
	vm_map_t           *map,
	vm_map_address_t    start,
	vm_map_size_t       size);

/*!
 * @function vm_map_range_ex_unlock()
 *
 * @brief
 * Releases an exclusive range lock successfully acquired
 * by @c vm_map_range_ex_lock().
 *
 * @param vml_ctx    the context passed to @c vm_map_range_ex_lock()
 * @param map        the original map passed to the lock call.
 *                   will be written into *map. This should be the original map
 *                   passed to the lock call. It can also be NULL.
 */
extern void vm_map_range_ex_unlock(
	vm_map_lock_ctx_t       vml_ctx,
	vm_map_t               *map);

/*!
 * @function vm_map_range_sh_unlock()
 *
 * @brief
 * Releases a shared range lock successfully acquired
 * by @c vm_map_range_sh_lock().
 *
 * @param vml_ctx    the context passed to @c vm_map_range_sh_lock()
 * @param map        the original map passed to the lock call.
 *                   will be written into *map. This should be the original map
 *                   passed to the lock call. It can also be NULL.
 */
extern void vm_map_range_sh_unlock(
	vm_map_lock_ctx_t       vml_ctx,
	vm_map_t               *map);


#pragma mark range iteration

/*!
 * @function vm_map_range_atomic_next()
 *
 * @abstract
 * Advance the cursor of the lock context.
 *
 * @discussion
 * This function updates the @c vmlc_vme field of the context
 * to the value it returns, locked shared or exclusive according
 * to how the lock was set up.
 *
 * @param vml_ctx       the locking context to use.
 */
extern vm_map_entry_t vm_map_range_atomic_next(
	vm_map_lock_ctx_t       vml_ctx) __result_use_check;

/*!
 * @function vm_map_range_atomic_peek
 *
 * @abstract
 * Returns the same entry that vm_map_range_atomic_next() would, but does not
 * advance the cursor of the lock context.
 *
 * @discussion
 * Unlike @c vm_map_range_atomic_next(), a successful call to this function does
 * not have any side effects, and may peek into an empty lock context.
 *
 * @param vml_ctx       the locking context to use.
 */
extern vm_map_entry_t vm_map_range_atomic_peek(
	vm_map_lock_ctx_t       vml_ctx) __result_use_check;

/*!
 * @function vm_map_range_ex_atomic_pop()
 *
 * @abstract
 * Advance the cursor of the lock context, and remove it from the range lock.
 *
 * @discussion
 * Unlike @c vm_map_range_next(), this function doesn't set
 * the @c vmlc_vme field of the context.
 *
 * Instead, it "removes" the current entry from the range lock and donates
 * it to the client, for the client to unlock. This is useful for interfaces
 * that remove entries from the map, and would confuse the range lock.
 *
 * The responsibility remains on the caller to remove the entry from the map,
 * this function only removes it from the range lock.
 *
 * Calls to @c vm_map_range_ex_atomic_pop should not be intermixed with calls to
 * @c vm_map_range_next or its variants.
 *
 * Note that calling @c vm_map_range_*_unlock() is still mandatory.
 *
 * @param vml_ctx       the locking context to use.
 */
extern vm_map_entry_t vm_map_range_ex_atomic_pop(
	vm_map_lock_ctx_t       vml_ctx) __result_use_check;

/*!
 * @function vm_map_range_atomic_reset()
 *
 * @brief
 * resets the iteration cursor of a range locked in atomic mode.
 *
 * @discussion
 * This is invalid for streaming locks.
 * It will panic due to assertions if called on a streaming lock.
 *
 * @param vml_ctx       the context passed to @c vm_map_range_ex_lock()
 */
extern void vm_map_range_atomic_reset(
	vm_map_lock_ctx_t       vml_ctx);


/*!
 * @function vm_map_range_stream_next_with_error()
 *
 * @abstract
 * Advance the cursor of the lock context.
 *
 * @discussion
 * This function updates the @c vmlc_vme field of the context
 * to the value it returns, locked shared or exclusive according
 * to how the lock was set up.
 *
 * @param vml_ctx
 * the locking context to use.
 *
 * @param kr
 * If a non NULL vm map entry is returned, then `kr` will always be
 * KERN_SUCCCES.
 *
 * Otherwise, the errors that can be returned are:
 *
 * - KERN_SUCCESS       a valid entry was returned,
 *                      or the enumeration finished successfully.
 *
 * - KERN_ABORTED       the range advance failed because some locking operation
 *                      was interrupted (VMRL_*_INTERRUPTIBLE only).
 *
 * - KERN_INVALID_ADDRESS
 *                      the range advance found a hole and stopped the iteration
 *                      (VMRL_STREAM_NO_HOLES only).
 *
 * - VMRL_ERR_LOCK_ALREADY_HELD
 *                      the lock wasn't acquired because the entry was already
 *                      locked. (VMRL_TRY_LOCK_ENTRY only)
 *
 * - other              the range advance failed because some entry preparation
 *                      failed with that error (VMRL_*_RESOLVE only).
 *
 * - other              the lock wasn't acquired because its preflight
 *                      rejected it (vmlc_preflight set only).
 */
extern vm_map_entry_t vm_map_range_stream_next_with_error(
	vm_map_lock_ctx_t       vml_ctx,
	kern_return_t          *kr __attribute__((nonnull))) __result_use_check;

/*!
 * @function vm_map_range_stream_next()
 *
 * @abstract
 * Advance the cursor of the lock context.
 *
 * @discussion
 * This function is a simpler version of @c vm_map_range_next(),
 * for cases when the caller knows it will never return an error.
 * If it would have, this function will panic.
 *
 * @param vml_ctx
 * the locking context to use.
 *
 */
extern vm_map_entry_t vm_map_range_stream_next(
	vm_map_lock_ctx_t       vml_ctx) __result_use_check;

/*!
 * @function vm_map_range_ex_stream_pop_with_error()
 *
 * @abstract
 * Advance the cursor of the lock context, and remove it from the range lock.
 *
 * @discussion
 * Unlike @c vm_map_range_stream_next(), this function doesn't set
 * the @c vmlc_vme field of the context.
 *
 * Instead, it "removes" the current entry from the range lock and donates
 * it to the client, for the client to unlock. This is useful for interfaces
 * that remove entries from the map, and would confuse the range lock.
 *
 * The responsibility remains on the caller to remove the entry from the map,
 * this function only removes it from the range lock.
 *
 * Note that calling @c vm_map_range_*_unlock() is still mandatory.
 *
 * @param vml_ctx       the locking context to use.
 */
extern vm_map_entry_t vm_map_range_ex_stream_pop_with_error(
	vm_map_lock_ctx_t       vml_ctx,
	kern_return_t          *kr __attribute__((nonnull))) __result_use_check;

/*!
 * @function vm_map_range_ex_stream_pop()
 *
 * @abstract
 * Advance the cursor of the lock context, and remove it from the range lock.
 *
 * @discussion
 * This is the same as vm_map_range_ex_stream_pop()
 * for cases when the caller knows that no error will be returned.
 * If it would have, this function will panic.
 */
extern vm_map_entry_t vm_map_range_ex_stream_pop(
	vm_map_lock_ctx_t       vml_ctx) __result_use_check;

/*!
 * @function vm_map_found_entry_ex_pop_curr()
 *
 * @brief
 * Remove an entry locked via @c vm_map_find_entry_ex_locked{,_or_next} from
 * the range lock. The entry remains locked, and it becomes the caller's
 * responsibility to unlock the entry.
 *
 * @param vml_ctx the context passed to
 *                @c vm_map_find_entry_ex_locked{,_or_next}().
 */
extern void vm_map_found_entry_ex_pop_curr(
	vm_map_find_lock_ctx_t  vml_ctx);

/*!
 * @function vm_map_range_stream_drop()
 *
 * @discussion
 * Allows clients of streaming locks to drop the lock on their current entry
 * (typically so they can perform slow, object-level operations) without
 * advancing to the next entry.
 *
 * Subsequent calls to @c vm_map_range_stream_next() are still permitted.
 * The next call to @c vm_map_range_stream_next() will advance to the entry
 * following the end of the entry being unlocked by this call.
 *
 * @param vml_ctx       the locking context to use.
 */
extern void vm_map_range_stream_drop(
	vm_map_lock_ctx_t       vml_ctx);

/*!
 * @function vm_map_range_stream_drop_without_advance()
 *
 * @discussion
 * Similar to @c vm_map_range_stream_drop, but the address the lock is
 * processing is not advanced to the end of the current entry.
 * Instead, the next call to @c vm_map_range_stream_next will lock the entry at
 * the start of the current iteration's lock bounds.
 * This function is used if you need to drop an entry lock for the same range -
 * it's generally uncommon but is used in faulting-like patterns sometimes.
 *
 * @param vml_ctx       the locking context to use.
 */
extern void vm_map_range_stream_drop_without_advance(
	vm_map_lock_ctx_t       vml_ctx);


/*!
 * @function vm_map_range_next_with_error()
 *
 * @abstract
 * Advance the cursor of the lock context.
 *
 * @discussion
 * This function updates the @c vmlc_vme field of the context
 * to the value it returns, locked shared or exclusive according
 * to how the lock was set up.
 *
 * Note: when the caller knows which kind of lock has been setup,
 *       using @c vm_map_range_atomic_next() or
 *       @c vm_map_range_stream_next() directly is preferred.
 *
 * @see @c vm_map_range_stream_next_with_error().
 * @see @c vm_map_range_atomic_next().
 *
 * @param vml_ctx       the locking context to use.
 * @param kr            an out parameter that contains an error.
 *                      @see vm_map_range_stream_next().
 */
extern vm_map_entry_t vm_map_range_next_with_error(
	vm_map_lock_ctx_t       vml_ctx,
	kern_return_t          *kr __attribute__((nonnull))) __result_use_check;

/*!
 * @function vm_map_range_next()
 *
 * @abstract
 * Advance the cursor of the lock context.
 *
 * @discussion
 * This is the same as vm_map_range_next_with_error()
 * for cases when the caller knows that no error will be returned.
 * If it would have, this function will panic.
 */
extern vm_map_entry_t vm_map_range_next(
	vm_map_lock_ctx_t       vml_ctx) __result_use_check;

/*!
 * @function vm_map_range_ex_pop_with_error()
 *
 * @abstract
 * Advance the cursor of the lock context, and remove it from the range lock.
 *
 * @discussion
 * Note: when the caller knows which kind of lock has been setup,
 *       using @c vm_map_range_ex_atomic_pop() or
 *       @c vm_map_range_ex_stream_pop() directly is preferred.
 *
 * @see @c vm_map_range_ex_stream_pop().
 * @see @c vm_map_range_ex_atomic_pop().
 *
 * @param vml_ctx       the locking context to use.
 * @param kr            an out parameter that contains an error.
 *                      @see vm_map_range_stream_next().
 */
extern vm_map_entry_t vm_map_range_ex_pop_with_error(
	vm_map_lock_ctx_t       vml_ctx,
	kern_return_t          *kr __attribute__((nonnull))) __result_use_check;

/*!
 * @function vm_map_range_ex_pop()
 *
 * @abstract
 * Advance the cursor of the lock context, and remove it from the range lock.
 *
 * @discussion
 * This is the same as vm_map_range_ex_pop_with_error()
 * for cases when the caller knows that no error will be returned.
 * If it would have, this function will panic.
 */
extern vm_map_entry_t vm_map_range_ex_pop(
	vm_map_lock_ctx_t       vml_ctx) __result_use_check;

/*!
 * @function vm_map_range_lock_clip_end
 *
 * @discussion
 * Splits a locked entry at @c endaddr.
 *
 * The entry passed in will be set to end at the given address,
 * and copy of the entry inserted after it, remaining locked.
 * If the address requested is after the entry, this function does nothing.
 *
 * The map should be unlocked.
 *
 * The entry must be owned exclusively by the caller.
 *
 * pmap unnesting must have happened prior
 *
 * @param ctx               the lock context
 * @param entry             the entry to be clipped
 * @param endaddr           the address to clip the entry to
 */
extern void vm_map_range_lock_clip_end(
	vm_map_lock_ctx_t       ctx,
	vm_map_entry_t          entry,
	vm_map_offset_t         endaddr);

/*!
 * @function vm_map_range_lock_clip_start
 *
 * @discussion
 * Clips an entry to start at a greater start address.
 *
 * The entry passed in will be set to start at the given address, and a copy
 * of the entry will be inserted before it.
 * If the address requested is after the entry, this function does nothing.
 *
 * The map should be unlocked.
 *
 * The entry must be owned exclusively by the caller.
 *
 * pmap unnesting must have happened prior
 *
 * @param ctx               the lock context
 * @param entry             the entry to be clipped
 * @param startaddr         the address to clip the entry to
 */
extern void vm_map_range_lock_clip_start(
	vm_map_lock_ctx_t       ctx,
	vm_map_entry_t          entry,
	vm_map_offset_t         startaddr);

/*!
 * @function vm_map_found_entry_clip_end_ilocked
 *
 * @discussion
 * Clips an entry locked via @c vm_map_find_entry_sh_locked{,_or_next} to end
 * at a lesser end address.
 *
 * The entry passed in will be set to end at the given address,
 * and a copy of the entry will be inserted after it.
 *
 * The entry must be owned exclusively by the caller.
 * The address requested to clip to must be less than the current end of the entry.
 *
 * Requires the original entry and interlock to be exclusively locked.
 * Returns with the original entry, interlock, and newly-created entry
 * exclusively locked.
 *
 * Since a "found entry" context necessarily contains only a single entry,
 * the new entry is NOT a part of the lock context and it is the caller's
 * responsibility to handle the new entry appropriately.
 *
 * pmap unnesting must have happened prior
 *
 * @param ctx               the context passed to vm_map_find_entry_sh_locked{,_or_next}
 * @param endaddr           the address to clip the entry to
 *
 * @returns a pointer to the newly created and locked entry
 */
extern vm_map_entry_t vm_map_found_entry_clip_end_ilocked(
	vm_map_find_lock_ctx_t  ctx,
	vm_map_offset_t         endaddr);

/*
 * @function vm_map_entry_lock_resolve_symmetric_cow
 *
 * @discussion
 * Resolve symmetric CoW for an entry with needs_copy. The entry must
 * have needs_copy.
 */
extern void vm_map_entry_lock_resolve_symmetric_cow(
	vm_map_t                map,
	vm_map_entry_t          entry);

/*
 * @function vm_map_entry_lock_allocate_object
 *
 * @discussion
 * Allocate a vm_object for an entry with no vm_object.
 */
extern void vm_map_entry_lock_allocate_object(
	vm_map_entry_t          entry,
	vm_map_serial_t         provenance);


#pragma mark Locking one entry at (or after) a fixed address (aka "Single Entry" lock)

/*!
 * @function vm_map_find_entry_sh_locked
 *
 * @brief
 * Share-lock a single entry at a given address.
 *
 * @discussion
 * If an entry exists such that
 * @c entry->vme_start <= @c addr < @c entry->vme_end
 * is is locked, in shared mode.
 *
 * @param vml_ctx the locking context to use.
 * @param map     the map being locked.
 *                This parameter will be NULLed on success to prevent it
 *                accidentally being used incorrectly by client code, which
 *                should instead use vm_map_lock_ctx_get_map()
 * @param addr    the address at which to look for an entry.
 * @param flags   a set of flags altering the lock behavior
 *                (VMRL_SHARED is implied).
 *
 * @returns
 * - KERN_SUCCESS
 *                      if an entry was successfully found and locked. The entry
 *                      can be found at @c vml_ctx->vmlc_vme.
 * - KERN_INVALID_ADDRESS
 *                      if no entry exists at @c addr in @c map.
 * - KERN_ABORTED
 *                      if the lock wasn't acquired because of the sleep
 *                      operation being interrupted (VMRL_SH_INTERRUPTIBLE
 *                      only).
 * - other
 *                      any error returned by the preflight hook.
 *
 * If any error is returned, vml_ctx public fields are not set and no lock is
 * acquired.
 */
__result_use_check
extern kern_return_t vm_map_find_entry_sh_locked(
	vm_map_find_lock_ctx_t  vml_ctx,
	vm_map_t               *map,
	vm_map_address_t        addr,
	vmrl_find_sh_flags_t    flags);

/*!
 * @function vm_map_find_entry_ex_locked
 *
 * @brief
 * Exclusively lock a single entry at a given address.
 *
 * @discussion
 * If an entry exists such that
 * @c entry->vme_start <= @c addr < @c entry->vme_end
 * it is exclusively locked.
 *
 * @param vml_ctx the locking context to use.
 * @param map     the map being locked.
 *                This parameter will be NULLed on success to prevent it
 *                accidentally being used incorrectly by client code, which
 *                should instead use vm_map_lock_ctx_get_map()
 * @param addr    the address at which to look for an entry.
 * @param flags   a set of flags altering the lock behavior
 *                (VMRL_EXCLUSIVE is implied).
 *
 * @returns
 * - KERN_SUCCESS
 *                      if an entry was successfully found and locked. The entry
 *                      can be found at @c vml_ctx->vmlc_vme.
 * - KERN_INVALID_ADDRESS
 *                      if no entry exists at @c addr in @c map.
 * - KERN_ABORTED
 *                      if the lock wasn't acquired because of the sleep
 *                      operation being interrupted (VMRL_EX_INTERRUPTIBLE
 *                      only).
 * - other
 *                      any error returned by the preflight hook.
 *
 * If any error is returned, vml_ctx public fields are not set and no lock is
 * acquired.
 */
__result_use_check
extern kern_return_t vm_map_find_entry_ex_locked(
	vm_map_find_lock_ctx_t  vml_ctx,
	vm_map_t               *map,
	vm_map_address_t        addr,
	vmrl_find_ex_flags_t    flags);

/*!
 * @function vm_map_found_entry_sh_unlock
 *
 * @brief
 * Unlock an entry locked via @c vm_map_find_entry_sh_locked{,_or_next}.
 *
 * @param vml_ctx    the context passed to
 *                   @c vm_map_find_entry_sh_locked{,_or_next}().
 * @param map     the original map passed to the lock call.
 *                will be written into *map. This should be the original map
 *                passed to the lock call. It can also be NULL.
 */
extern void vm_map_found_entry_sh_unlock(
	vm_map_find_lock_ctx_t  vml_ctx,
	vm_map_t               *map);

/*!
 * @function vm_map_found_entry_ex_unlock
 *
 * @brief
 * Unlock an entry locked via @c vm_map_find_entry_ex_locked{,_or_next}.
 *
 * @param vml_ctx the context passed to
 *                @c vm_map_find_entry_ex_locked{,_or_next}().
 * @param map     the original map passed to the lock call.
 *                will be written into *map. This should be the original map
 *                passed to the lock call. It can also be NULL.
 */
extern void vm_map_found_entry_ex_unlock(
	vm_map_find_lock_ctx_t  vml_ctx,
	vm_map_t               *map);

/*!
 * @function vm_map_lock_ctx_is_descended
 *
 * @brief
 * Return if a lock context is descended into a submap
 */
static inline bool
vm_map_lock_ctx_is_descended(vm_map_lock_ctx_t ctx)
{
	return ctx->__vmlc_descended != VMLC_NOT_DESCENDED;
}

/*!
 * @function vm_map_lock_ctx_in_constant_submap
 *
 * @brief
 * Return if a lock context is descended into a constant submap
 */
static inline bool
vm_map_lock_ctx_in_constant_submap(vm_map_lock_ctx_t ctx)
{
	return ctx->__vmlc_descended == VMLC_IN_CONSTANT_SUBMAP;
}

/*!
 * @function vm_map_lock_ctx_get_map
 *
 * @brief
 * Returns the current vm_map_t the iteration is at
 * This may be different from the map the lock was called on
 * in case the lock is descended into a submap.
 */
static inline vm_map_t
vm_map_lock_ctx_get_map(vm_map_lock_ctx_t ctx)
{
	return ctx->vmlc_map;
}

/*!
 * @function vm_map_found_entry_get_entry
 *
 * @brief
 * For a find entry lock, return the currently locked entry.
 * The ctx must be locked.
 */
static inline vm_map_entry_t
vm_map_found_entry_get_entry(vm_map_find_lock_ctx_t ctx)
{
	assert(ctx->__vmlc_locked);
	return ctx->vmlc_vme;
}

/*!
 * @function vm_map_lock_ctx_is_in_needs_copy_submap
 *
 * @brief
 * Return whether the lock context is currently descended into a
 * needs_copy submap.
 */
static inline bool
vm_map_lock_ctx_is_in_needs_copy_submap(vm_map_lock_ctx_t ctx)
{
	if (vm_map_lock_ctx_is_descended(ctx)) {
		return ctx->__parent_entry->needs_copy;
	}
	return false;
}

/*!
 * @function vm_map_lock_ctx_is_in_pmap_nested_submap
 *
 * @brief
 * Return whether the lock context is currently descended into a submap with
 * a nested pmap.
 * See the comment in @vm_range_lock_pmap_unnest_and_clip for more info about
 * what that means.
 */
static inline bool
vm_map_lock_ctx_is_in_pmap_nested_submap(vm_map_lock_ctx_t ctx)
{
	if (vm_map_lock_ctx_is_descended(ctx)) {
		return ctx->__parent_entry->use_pmap;
	}
	return false;
}

/*
 * @function vm_map_lock_ctx_get_parent_entry_window
 *
 * @brief
 * This function should likely only be used for vm_map_region_recurse.
 * It gives the window that the parent entry gives into the submap, in
 * the address coordinates of the submap.
 *
 * *IMPORTANT*
 * @warning
 *  This is regardless of the bounds the lock ctx asked for. That means these
 * bounds may be before/after the requested bounds of the lock ctx
 * (vmlc_req_start/end).
 * That's because this is what vm_map_region_recurse needs, but it is unlikely
 * any other functions would want to go backwards from the range requested.
 */
static inline void
vm_map_lock_ctx_get_parent_entry_window(
	vm_map_lock_ctx_t       ctx,
	vm_map_address_t       *startp,
	vm_map_address_t       *endp)
{
	assert(vm_map_lock_ctx_is_descended(ctx));
	*startp = vm_map_lock_ctx_from_parent_address(ctx, ctx->__parent_entry->vme_start);
	*endp = vm_map_lock_ctx_from_parent_address(ctx, ctx->__parent_entry->vme_end);
}

/*!
 * @function vm_map_range_ex_lock_add_flags
 *
 * @brief
 * Add flags to an exclusive vm_map_range lock.
 */
extern void vm_map_range_ex_lock_add_flags(
	vm_map_lock_ctx_t vml_ctx,
	vmrl_ex_flags_t flags);

/*!
 * @function vm_map_range_ex_lock_remove_flags
 *
 * @brief
 * Remove flags from an exclusive vm_map_range lock.
 */
extern void vm_map_range_ex_lock_remove_flags(
	vm_map_lock_ctx_t vml_ctx,
	vmrl_ex_flags_t flags);

/*!
 * @function vm_map_range_sh_lock_add_flags
 *
 * @brief
 * Add flags to a shared vm_map_range lock.
 */
extern void vm_map_range_sh_lock_add_flags(
	vm_map_lock_ctx_t vml_ctx,
	vmrl_sh_flags_t flags);

/*!
 * @function vm_map_range_ex_lock_remove_flags
 *
 * @brief
 * Remove flags from a shared vm_map_range lock.
 */
extern void vm_map_range_sh_lock_remove_flags(
	vm_map_lock_ctx_t vml_ctx,
	vmrl_sh_flags_t flags);

#pragma mark Simplification

/*!
 * @function vm_map_locked_entry_simplify
 *
 * @brief
 * Attempts to coalesce the provided entry with its neighbors.
 *
 * @warning This is intended for use by functions engaging in manual locking
 *          only. Clients using the range lock should use VMRL_SIMPLIFY. Clients
 *          using the single entry lock (vm_map_find_entry_*) do not need to
 *          simplify today. If the need arises, they should implement
 *          VMRL_SIMPLIFY support for the single entry lock instead of
 *          attempting to make this work with the single entry lock.
 *
 * @param [in] map    The map in which simplification will take place. Its
 *                    interlock should not be held.
 * @param [in] entry  The entry to be simplified. It should be locked. Its
 *                    neighboring entries should not have their locks held
 *                    by the caller.
 *
 * @return The entry resulting from the simplification. It may not be the same
 *         as the one passed in (see rdar://150789194). Callers should stop
 *         using the entry pointer they passed in, and work on this returned
 *         entry instead. It will be locked. No other locks will be returned
 *         held.
 */
__result_use_check
extern vm_map_entry_t
vm_map_locked_entry_simplify(
	vm_map_t                map,
	vm_map_entry_t          entry);


__END_DECLS

#endif /* _VM_VM_MAP_LOCK_H_ */