Subversion Repositories HelenOS-historic

Rev

Rev 1760 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1760 Rev 1780
Line 95... Line 95...
95
 
95
 
96
/** Kernel address space. */
96
/** Kernel address space. */
97
as_t *AS_KERNEL = NULL;
97
as_t *AS_KERNEL = NULL;
98
 
98
 
99
static int area_flags_to_page_flags(int aflags);
99
static int area_flags_to_page_flags(int aflags);
100
static as_area_t *find_area_and_lock(as_t *as, __address va);
100
static as_area_t *find_area_and_lock(as_t *as, uintptr_t va);
101
static bool check_area_conflicts(as_t *as, __address va, size_t size, as_area_t *avoid_area);
101
static bool check_area_conflicts(as_t *as, uintptr_t va, size_t size, as_area_t *avoid_area);
102
static void sh_info_remove_reference(share_info_t *sh_info);
102
static void sh_info_remove_reference(share_info_t *sh_info);
103
 
103
 
104
/** Initialize address space subsystem. */
104
/** Initialize address space subsystem. */
105
void as_init(void)
105
void as_init(void)
106
{
106
{
Line 197... Line 197...
197
 * @param backend Address space area backend. NULL if no backend is used.
197
 * @param backend Address space area backend. NULL if no backend is used.
198
 * @param backend_data NULL or a pointer to an array holding two void *.
198
 * @param backend_data NULL or a pointer to an array holding two void *.
199
 *
199
 *
200
 * @return Address space area on success or NULL on failure.
200
 * @return Address space area on success or NULL on failure.
201
 */
201
 */
202
as_area_t *as_area_create(as_t *as, int flags, size_t size, __address base, int attrs,
202
as_area_t *as_area_create(as_t *as, int flags, size_t size, uintptr_t base, int attrs,
203
           mem_backend_t *backend, mem_backend_data_t *backend_data)
203
           mem_backend_t *backend, mem_backend_data_t *backend_data)
204
{
204
{
205
    ipl_t ipl;
205
    ipl_t ipl;
206
    as_area_t *a;
206
    as_area_t *a;
207
   
207
   
Line 236... Line 236...
236
    a->sh_info = NULL;
236
    a->sh_info = NULL;
237
    a->backend = backend;
237
    a->backend = backend;
238
    if (backend_data)
238
    if (backend_data)
239
        a->backend_data = *backend_data;
239
        a->backend_data = *backend_data;
240
    else
240
    else
241
        memsetb((__address) &a->backend_data, sizeof(a->backend_data), 0);
241
        memsetb((uintptr_t) &a->backend_data, sizeof(a->backend_data), 0);
242
 
242
 
243
    btree_create(&a->used_space);
243
    btree_create(&a->used_space);
244
   
244
   
245
    btree_insert(&as->as_area_btree, base, (void *) a, NULL);
245
    btree_insert(&as->as_area_btree, base, (void *) a, NULL);
246
 
246
 
Line 257... Line 257...
257
 * @param size New size of the virtual memory block starting at address.
257
 * @param size New size of the virtual memory block starting at address.
258
 * @param flags Flags influencing the remap operation. Currently unused.
258
 * @param flags Flags influencing the remap operation. Currently unused.
259
 *
259
 *
260
 * @return Zero on success or a value from @ref errno.h otherwise.
260
 * @return Zero on success or a value from @ref errno.h otherwise.
261
 */
261
 */
262
int as_area_resize(as_t *as, __address address, size_t size, int flags)
262
int as_area_resize(as_t *as, uintptr_t address, size_t size, int flags)
263
{
263
{
264
    as_area_t *area;
264
    as_area_t *area;
265
    ipl_t ipl;
265
    ipl_t ipl;
266
    size_t pages;
266
    size_t pages;
267
   
267
   
Line 310... Line 310...
310
        return EPERM;
310
        return EPERM;
311
    }
311
    }
312
   
312
   
313
    if (pages < area->pages) {
313
    if (pages < area->pages) {
314
        bool cond;
314
        bool cond;
315
        __address start_free = area->base + pages*PAGE_SIZE;
315
        uintptr_t start_free = area->base + pages*PAGE_SIZE;
316
 
316
 
317
        /*
317
        /*
318
         * Shrinking the area.
318
         * Shrinking the area.
319
         * No need to check for overlaps.
319
         * No need to check for overlaps.
320
         */
320
         */
Line 335... Line 335...
335
            btree_node_t *node;
335
            btree_node_t *node;
336
       
336
       
337
            ASSERT(!list_empty(&area->used_space.leaf_head));
337
            ASSERT(!list_empty(&area->used_space.leaf_head));
338
            node = list_get_instance(area->used_space.leaf_head.prev, btree_node_t, leaf_link);
338
            node = list_get_instance(area->used_space.leaf_head.prev, btree_node_t, leaf_link);
339
            if ((cond = (bool) node->keys)) {
339
            if ((cond = (bool) node->keys)) {
340
                __address b = node->key[node->keys - 1];
340
                uintptr_t b = node->key[node->keys - 1];
341
                count_t c = (count_t) node->value[node->keys - 1];
341
                count_t c = (count_t) node->value[node->keys - 1];
342
                int i = 0;
342
                int i = 0;
343
           
343
           
344
                if (overlaps(b, c*PAGE_SIZE, area->base, pages*PAGE_SIZE)) {
344
                if (overlaps(b, c*PAGE_SIZE, area->base, pages*PAGE_SIZE)) {
345
                   
345
                   
Line 416... Line 416...
416
 * @param as Address space.
416
 * @param as Address space.
417
 * @param address Address withing the area to be deleted.
417
 * @param address Address withing the area to be deleted.
418
 *
418
 *
419
 * @return Zero on success or a value from @ref errno.h on failure.
419
 * @return Zero on success or a value from @ref errno.h on failure.
420
 */
420
 */
421
int as_area_destroy(as_t *as, __address address)
421
int as_area_destroy(as_t *as, uintptr_t address)
422
{
422
{
423
    as_area_t *area;
423
    as_area_t *area;
424
    __address base;
424
    uintptr_t base;
425
    link_t *cur;
425
    link_t *cur;
426
    ipl_t ipl;
426
    ipl_t ipl;
427
 
427
 
428
    ipl = interrupts_disable();
428
    ipl = interrupts_disable();
429
    mutex_lock(&as->lock);
429
    mutex_lock(&as->lock);
Line 449... Line 449...
449
        btree_node_t *node;
449
        btree_node_t *node;
450
        int i;
450
        int i;
451
       
451
       
452
        node = list_get_instance(cur, btree_node_t, leaf_link);
452
        node = list_get_instance(cur, btree_node_t, leaf_link);
453
        for (i = 0; i < node->keys; i++) {
453
        for (i = 0; i < node->keys; i++) {
454
            __address b = node->key[i];
454
            uintptr_t b = node->key[i];
455
            count_t j;
455
            count_t j;
456
            pte_t *pte;
456
            pte_t *pte;
457
           
457
           
458
            for (j = 0; j < (count_t) node->value[i]; j++) {
458
            for (j = 0; j < (count_t) node->value[i]; j++) {
459
                page_table_lock(as, false);
459
                page_table_lock(as, false);
Line 516... Line 516...
516
 *     EPERM if there was a problem in accepting the area or
516
 *     EPERM if there was a problem in accepting the area or
517
 *     ENOMEM if there was a problem in allocating destination
517
 *     ENOMEM if there was a problem in allocating destination
518
 *     address space area. ENOTSUP is returned if an attempt
518
 *     address space area. ENOTSUP is returned if an attempt
519
 *     to share non-anonymous address space area is detected.
519
 *     to share non-anonymous address space area is detected.
520
 */
520
 */
521
int as_area_share(as_t *src_as, __address src_base, size_t acc_size,
521
int as_area_share(as_t *src_as, uintptr_t src_base, size_t acc_size,
522
          as_t *dst_as, __address dst_base, int dst_flags_mask)
522
          as_t *dst_as, uintptr_t dst_base, int dst_flags_mask)
523
{
523
{
524
    ipl_t ipl;
524
    ipl_t ipl;
525
    int src_flags;
525
    int src_flags;
526
    size_t src_size;
526
    size_t src_size;
527
    as_area_t *src_area, *dst_area;
527
    as_area_t *src_area, *dst_area;
Line 663... Line 663...
663
 * @param istate Pointer to interrupted state.
663
 * @param istate Pointer to interrupted state.
664
 *
664
 *
665
 * @return AS_PF_FAULT on page fault, AS_PF_OK on success or AS_PF_DEFER if the
665
 * @return AS_PF_FAULT on page fault, AS_PF_OK on success or AS_PF_DEFER if the
666
 *     fault was caused by copy_to_uspace() or copy_from_uspace().
666
 *     fault was caused by copy_to_uspace() or copy_from_uspace().
667
 */
667
 */
668
int as_page_fault(__address page, pf_access_t access, istate_t *istate)
668
int as_page_fault(uintptr_t page, pf_access_t access, istate_t *istate)
669
{
669
{
670
    pte_t *pte;
670
    pte_t *pte;
671
    as_area_t *area;
671
    as_area_t *area;
672
   
672
   
673
    if (!THREAD)
673
    if (!THREAD)
Line 742... Line 742...
742
    return AS_PF_OK;
742
    return AS_PF_OK;
743
 
743
 
744
page_fault:
744
page_fault:
745
    if (THREAD->in_copy_from_uspace) {
745
    if (THREAD->in_copy_from_uspace) {
746
        THREAD->in_copy_from_uspace = false;
746
        THREAD->in_copy_from_uspace = false;
747
        istate_set_retaddr(istate, (__address) &memcpy_from_uspace_failover_address);
747
        istate_set_retaddr(istate, (uintptr_t) &memcpy_from_uspace_failover_address);
748
    } else if (THREAD->in_copy_to_uspace) {
748
    } else if (THREAD->in_copy_to_uspace) {
749
        THREAD->in_copy_to_uspace = false;
749
        THREAD->in_copy_to_uspace = false;
750
        istate_set_retaddr(istate, (__address) &memcpy_to_uspace_failover_address);
750
        istate_set_retaddr(istate, (uintptr_t) &memcpy_to_uspace_failover_address);
751
    } else {
751
    } else {
752
        return AS_PF_FAULT;
752
        return AS_PF_FAULT;
753
    }
753
    }
754
 
754
 
755
    return AS_PF_DEFER;
755
    return AS_PF_DEFER;
Line 940... Line 940...
940
 * @param as Address space.
940
 * @param as Address space.
941
 * @param va Virtual address.
941
 * @param va Virtual address.
942
 *
942
 *
943
 * @return Locked address space area containing va on success or NULL on failure.
943
 * @return Locked address space area containing va on success or NULL on failure.
944
 */
944
 */
945
as_area_t *find_area_and_lock(as_t *as, __address va)
945
as_area_t *find_area_and_lock(as_t *as, uintptr_t va)
946
{
946
{
947
    as_area_t *a;
947
    as_area_t *a;
948
    btree_node_t *leaf, *lnode;
948
    btree_node_t *leaf, *lnode;
949
    int i;
949
    int i;
950
   
950
   
Line 996... Line 996...
996
 * @param size Size of the area being tested.
996
 * @param size Size of the area being tested.
997
 * @param avoid_area Do not touch this area.
997
 * @param avoid_area Do not touch this area.
998
 *
998
 *
999
 * @return True if there is no conflict, false otherwise.
999
 * @return True if there is no conflict, false otherwise.
1000
 */
1000
 */
1001
bool check_area_conflicts(as_t *as, __address va, size_t size, as_area_t *avoid_area)
1001
bool check_area_conflicts(as_t *as, uintptr_t va, size_t size, as_area_t *avoid_area)
1002
{
1002
{
1003
    as_area_t *a;
1003
    as_area_t *a;
1004
    btree_node_t *leaf, *node;
1004
    btree_node_t *leaf, *node;
1005
    int i;
1005
    int i;
1006
   
1006
   
Line 1069... Line 1069...
1069
 
1069
 
1070
    return true;
1070
    return true;
1071
}
1071
}
1072
 
1072
 
1073
/** Return size of the address space area with given base.  */
1073
/** Return size of the address space area with given base.  */
1074
size_t as_get_size(__address base)
1074
size_t as_get_size(uintptr_t base)
1075
{
1075
{
1076
    ipl_t ipl;
1076
    ipl_t ipl;
1077
    as_area_t *src_area;
1077
    as_area_t *src_area;
1078
    size_t size;
1078
    size_t size;
1079
 
1079
 
Line 1097... Line 1097...
1097
 * @param page First page to be marked.
1097
 * @param page First page to be marked.
1098
 * @param count Number of page to be marked.
1098
 * @param count Number of page to be marked.
1099
 *
1099
 *
1100
 * @return 0 on failure and 1 on success.
1100
 * @return 0 on failure and 1 on success.
1101
 */
1101
 */
1102
int used_space_insert(as_area_t *a, __address page, count_t count)
1102
int used_space_insert(as_area_t *a, uintptr_t page, count_t count)
1103
{
1103
{
1104
    btree_node_t *leaf, *node;
1104
    btree_node_t *leaf, *node;
1105
    count_t pages;
1105
    count_t pages;
1106
    int i;
1106
    int i;
1107
 
1107
 
Line 1121... Line 1121...
1121
        return 1;
1121
        return 1;
1122
    }
1122
    }
1123
 
1123
 
1124
    node = btree_leaf_node_left_neighbour(&a->used_space, leaf);
1124
    node = btree_leaf_node_left_neighbour(&a->used_space, leaf);
1125
    if (node) {
1125
    if (node) {
1126
        __address left_pg = node->key[node->keys - 1], right_pg = leaf->key[0];
1126
        uintptr_t left_pg = node->key[node->keys - 1], right_pg = leaf->key[0];
1127
        count_t left_cnt = (count_t) node->value[node->keys - 1], right_cnt = (count_t) leaf->value[0];
1127
        count_t left_cnt = (count_t) node->value[node->keys - 1], right_cnt = (count_t) leaf->value[0];
1128
       
1128
       
1129
        /*
1129
        /*
1130
         * Examine the possibility that the interval fits
1130
         * Examine the possibility that the interval fits
1131
         * somewhere between the rightmost interval of
1131
         * somewhere between the rightmost interval of
Line 1164... Line 1164...
1164
             */
1164
             */
1165
            btree_insert(&a->used_space, page, (void *) count, leaf);
1165
            btree_insert(&a->used_space, page, (void *) count, leaf);
1166
            return 1;
1166
            return 1;
1167
        }
1167
        }
1168
    } else if (page < leaf->key[0]) {
1168
    } else if (page < leaf->key[0]) {
1169
        __address right_pg = leaf->key[0];
1169
        uintptr_t right_pg = leaf->key[0];
1170
        count_t right_cnt = (count_t) leaf->value[0];
1170
        count_t right_cnt = (count_t) leaf->value[0];
1171
   
1171
   
1172
        /*
1172
        /*
1173
         * Investigate the border case in which the left neighbour does not
1173
         * Investigate the border case in which the left neighbour does not
1174
         * exist but the interval fits from the left.
1174
         * exist but the interval fits from the left.
Line 1195... Line 1195...
1195
        }
1195
        }
1196
    }
1196
    }
1197
 
1197
 
1198
    node = btree_leaf_node_right_neighbour(&a->used_space, leaf);
1198
    node = btree_leaf_node_right_neighbour(&a->used_space, leaf);
1199
    if (node) {
1199
    if (node) {
1200
        __address left_pg = leaf->key[leaf->keys - 1], right_pg = node->key[0];
1200
        uintptr_t left_pg = leaf->key[leaf->keys - 1], right_pg = node->key[0];
1201
        count_t left_cnt = (count_t) leaf->value[leaf->keys - 1], right_cnt = (count_t) node->value[0];
1201
        count_t left_cnt = (count_t) leaf->value[leaf->keys - 1], right_cnt = (count_t) node->value[0];
1202
       
1202
       
1203
        /*
1203
        /*
1204
         * Examine the possibility that the interval fits
1204
         * Examine the possibility that the interval fits
1205
         * somewhere between the leftmost interval of
1205
         * somewhere between the leftmost interval of
Line 1238... Line 1238...
1238
             */
1238
             */
1239
            btree_insert(&a->used_space, page, (void *) count, leaf);
1239
            btree_insert(&a->used_space, page, (void *) count, leaf);
1240
            return 1;
1240
            return 1;
1241
        }
1241
        }
1242
    } else if (page >= leaf->key[leaf->keys - 1]) {
1242
    } else if (page >= leaf->key[leaf->keys - 1]) {
1243
        __address left_pg = leaf->key[leaf->keys - 1];
1243
        uintptr_t left_pg = leaf->key[leaf->keys - 1];
1244
        count_t left_cnt = (count_t) leaf->value[leaf->keys - 1];
1244
        count_t left_cnt = (count_t) leaf->value[leaf->keys - 1];
1245
   
1245
   
1246
        /*
1246
        /*
1247
         * Investigate the border case in which the right neighbour does not
1247
         * Investigate the border case in which the right neighbour does not
1248
         * exist but the interval fits from the right.
1248
         * exist but the interval fits from the right.
Line 1270... Line 1270...
1270
     * between two other intervals of the leaf. The two border cases were already
1270
     * between two other intervals of the leaf. The two border cases were already
1271
     * resolved.
1271
     * resolved.
1272
     */
1272
     */
1273
    for (i = 1; i < leaf->keys; i++) {
1273
    for (i = 1; i < leaf->keys; i++) {
1274
        if (page < leaf->key[i]) {
1274
        if (page < leaf->key[i]) {
1275
            __address left_pg = leaf->key[i - 1], right_pg = leaf->key[i];
1275
            uintptr_t left_pg = leaf->key[i - 1], right_pg = leaf->key[i];
1276
            count_t left_cnt = (count_t) leaf->value[i - 1], right_cnt = (count_t) leaf->value[i];
1276
            count_t left_cnt = (count_t) leaf->value[i - 1], right_cnt = (count_t) leaf->value[i];
1277
 
1277
 
1278
            /*
1278
            /*
1279
             * The interval fits between left_pg and right_pg.
1279
             * The interval fits between left_pg and right_pg.
1280
             */
1280
             */
Line 1324... Line 1324...
1324
 * @param page First page to be marked.
1324
 * @param page First page to be marked.
1325
 * @param count Number of page to be marked.
1325
 * @param count Number of page to be marked.
1326
 *
1326
 *
1327
 * @return 0 on failure and 1 on success.
1327
 * @return 0 on failure and 1 on success.
1328
 */
1328
 */
1329
int used_space_remove(as_area_t *a, __address page, count_t count)
1329
int used_space_remove(as_area_t *a, uintptr_t page, count_t count)
1330
{
1330
{
1331
    btree_node_t *leaf, *node;
1331
    btree_node_t *leaf, *node;
1332
    count_t pages;
1332
    count_t pages;
1333
    int i;
1333
    int i;
1334
 
1334
 
Line 1361... Line 1361...
1361
        }
1361
        }
1362
    }
1362
    }
1363
 
1363
 
1364
    node = btree_leaf_node_left_neighbour(&a->used_space, leaf);
1364
    node = btree_leaf_node_left_neighbour(&a->used_space, leaf);
1365
    if (node && page < leaf->key[0]) {
1365
    if (node && page < leaf->key[0]) {
1366
        __address left_pg = node->key[node->keys - 1];
1366
        uintptr_t left_pg = node->key[node->keys - 1];
1367
        count_t left_cnt = (count_t) node->value[node->keys - 1];
1367
        count_t left_cnt = (count_t) node->value[node->keys - 1];
1368
 
1368
 
1369
        if (overlaps(left_pg, left_cnt*PAGE_SIZE, page, count*PAGE_SIZE)) {
1369
        if (overlaps(left_pg, left_cnt*PAGE_SIZE, page, count*PAGE_SIZE)) {
1370
            if (page + count*PAGE_SIZE == left_pg + left_cnt*PAGE_SIZE) {
1370
            if (page + count*PAGE_SIZE == left_pg + left_cnt*PAGE_SIZE) {
1371
                /*
1371
                /*
Line 1394... Line 1394...
1394
    } else if (page < leaf->key[0]) {
1394
    } else if (page < leaf->key[0]) {
1395
        return 0;
1395
        return 0;
1396
    }
1396
    }
1397
   
1397
   
1398
    if (page > leaf->key[leaf->keys - 1]) {
1398
    if (page > leaf->key[leaf->keys - 1]) {
1399
        __address left_pg = leaf->key[leaf->keys - 1];
1399
        uintptr_t left_pg = leaf->key[leaf->keys - 1];
1400
        count_t left_cnt = (count_t) leaf->value[leaf->keys - 1];
1400
        count_t left_cnt = (count_t) leaf->value[leaf->keys - 1];
1401
 
1401
 
1402
        if (overlaps(left_pg, left_cnt*PAGE_SIZE, page, count*PAGE_SIZE)) {
1402
        if (overlaps(left_pg, left_cnt*PAGE_SIZE, page, count*PAGE_SIZE)) {
1403
            if (page + count*PAGE_SIZE == left_pg + left_cnt*PAGE_SIZE) {
1403
            if (page + count*PAGE_SIZE == left_pg + left_cnt*PAGE_SIZE) {
1404
                /*
1404
                /*
Line 1430... Line 1430...
1430
     * The border cases have been already resolved.
1430
     * The border cases have been already resolved.
1431
     * Now the interval can be only between intervals of the leaf.
1431
     * Now the interval can be only between intervals of the leaf.
1432
     */
1432
     */
1433
    for (i = 1; i < leaf->keys - 1; i++) {
1433
    for (i = 1; i < leaf->keys - 1; i++) {
1434
        if (page < leaf->key[i]) {
1434
        if (page < leaf->key[i]) {
1435
            __address left_pg = leaf->key[i - 1];
1435
            uintptr_t left_pg = leaf->key[i - 1];
1436
            count_t left_cnt = (count_t) leaf->value[i - 1];
1436
            count_t left_cnt = (count_t) leaf->value[i - 1];
1437
 
1437
 
1438
            /*
1438
            /*
1439
             * Now the interval is between intervals corresponding to (i - 1) and i.
1439
             * Now the interval is between intervals corresponding to (i - 1) and i.
1440
             */
1440
             */
Line 1494... Line 1494...
1494
            btree_node_t *node;
1494
            btree_node_t *node;
1495
            int i;
1495
            int i;
1496
           
1496
           
1497
            node = list_get_instance(cur, btree_node_t, leaf_link);
1497
            node = list_get_instance(cur, btree_node_t, leaf_link);
1498
            for (i = 0; i < node->keys; i++)
1498
            for (i = 0; i < node->keys; i++)
1499
                frame_free((__address) node->value[i]);
1499
                frame_free((uintptr_t) node->value[i]);
1500
        }
1500
        }
1501
       
1501
       
1502
    }
1502
    }
1503
    mutex_unlock(&sh_info->lock);
1503
    mutex_unlock(&sh_info->lock);
1504
   
1504
   
Line 1511... Line 1511...
1511
/*
1511
/*
1512
 * Address space related syscalls.
1512
 * Address space related syscalls.
1513
 */
1513
 */
1514
 
1514
 
1515
/** Wrapper for as_area_create(). */
1515
/** Wrapper for as_area_create(). */
1516
__native sys_as_area_create(__address address, size_t size, int flags)
1516
unative_t sys_as_area_create(uintptr_t address, size_t size, int flags)
1517
{
1517
{
1518
    if (as_area_create(AS, flags | AS_AREA_CACHEABLE, size, address, AS_AREA_ATTR_NONE, &anon_backend, NULL))
1518
    if (as_area_create(AS, flags | AS_AREA_CACHEABLE, size, address, AS_AREA_ATTR_NONE, &anon_backend, NULL))
1519
        return (__native) address;
1519
        return (unative_t) address;
1520
    else
1520
    else
1521
        return (__native) -1;
1521
        return (unative_t) -1;
1522
}
1522
}
1523
 
1523
 
1524
/** Wrapper for as_area_resize. */
1524
/** Wrapper for as_area_resize. */
1525
__native sys_as_area_resize(__address address, size_t size, int flags)
1525
unative_t sys_as_area_resize(uintptr_t address, size_t size, int flags)
1526
{
1526
{
1527
    return (__native) as_area_resize(AS, address, size, 0);
1527
    return (unative_t) as_area_resize(AS, address, size, 0);
1528
}
1528
}
1529
 
1529
 
1530
/** Wrapper for as_area_destroy. */
1530
/** Wrapper for as_area_destroy. */
1531
__native sys_as_area_destroy(__address address)
1531
unative_t sys_as_area_destroy(uintptr_t address)
1532
{
1532
{
1533
    return (__native) as_area_destroy(AS, address);
1533
    return (unative_t) as_area_destroy(AS, address);
1534
}
1534
}
1535
 
1535
 
1536
/** @}
1536
/** @}
1537
 */
1537
 */