OpenCores
URL https://opencores.org/ocsvn/or1k_soc_on_altera_embedded_dev_kit/or1k_soc_on_altera_embedded_dev_kit/trunk

Subversion Repositories or1k_soc_on_altera_embedded_dev_kit

[/] [or1k_soc_on_altera_embedded_dev_kit/] [trunk/] [linux-2.6/] [linux-2.6.24/] [arch/] [s390/] [mm/] [extmem.c] - Blame information for rev 3

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 3 xianfeng
/*
2
 * File...........: arch/s390/mm/extmem.c
3
 * Author(s)......: Carsten Otte <cotte@de.ibm.com>
4
 *                  Rob M van der Heij <rvdheij@nl.ibm.com>
5
 *                  Steven Shultz <shultzss@us.ibm.com>
6
 * Bugreports.to..: <Linux390@de.ibm.com>
7
 * (C) IBM Corporation 2002-2004
8
 */
9
 
10
#include <linux/kernel.h>
11
#include <linux/string.h>
12
#include <linux/spinlock.h>
13
#include <linux/list.h>
14
#include <linux/slab.h>
15
#include <linux/module.h>
16
#include <linux/bootmem.h>
17
#include <linux/ctype.h>
18
#include <linux/ioport.h>
19
#include <asm/page.h>
20
#include <asm/pgtable.h>
21
#include <asm/ebcdic.h>
22
#include <asm/errno.h>
23
#include <asm/extmem.h>
24
#include <asm/cpcmd.h>
25
#include <asm/setup.h>
26
 
27
#define DCSS_DEBUG      /* Debug messages on/off */
28
 
29
#define DCSS_NAME "extmem"
30
#ifdef DCSS_DEBUG
31
#define PRINT_DEBUG(x...)       printk(KERN_DEBUG DCSS_NAME " debug:" x)
32
#else
33
#define PRINT_DEBUG(x...)   do {} while (0)
34
#endif
35
#define PRINT_INFO(x...)        printk(KERN_INFO DCSS_NAME " info:" x)
36
#define PRINT_WARN(x...)        printk(KERN_WARNING DCSS_NAME " warning:" x)
37
#define PRINT_ERR(x...)         printk(KERN_ERR DCSS_NAME " error:" x)
38
 
39
 
40
#define DCSS_LOADSHR    0x00
41
#define DCSS_LOADNSR    0x04
42
#define DCSS_PURGESEG   0x08
43
#define DCSS_FINDSEG    0x0c
44
#define DCSS_LOADNOLY   0x10
45
#define DCSS_SEGEXT     0x18
46
#define DCSS_FINDSEGA   0x0c
47
 
48
struct qrange {
49
        unsigned int  start; // 3byte start address, 1 byte type
50
        unsigned int  end;   // 3byte end address, 1 byte reserved
51
};
52
 
53
struct qout64 {
54
        int segstart;
55
        int segend;
56
        int segcnt;
57
        int segrcnt;
58
        struct qrange range[6];
59
};
60
 
61
struct qin64 {
62
        char qopcode;
63
        char rsrv1[3];
64
        char qrcode;
65
        char rsrv2[3];
66
        char qname[8];
67
        unsigned int qoutptr;
68
        short int qoutlen;
69
};
70
 
71
struct dcss_segment {
72
        struct list_head list;
73
        char dcss_name[8];
74
        char res_name[15];
75
        unsigned long start_addr;
76
        unsigned long end;
77
        atomic_t ref_count;
78
        int do_nonshared;
79
        unsigned int vm_segtype;
80
        struct qrange range[6];
81
        int segcnt;
82
        struct resource *res;
83
};
84
 
85
static DEFINE_MUTEX(dcss_lock);
86
static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list);
87
static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
88
                                        "EW/EN-MIXED" };
89
 
90
/*
91
 * Create the 8 bytes, ebcdic VM segment name from
92
 * an ascii name.
93
 */
94
static void
95
dcss_mkname(char *name, char *dcss_name)
96
{
97
        int i;
98
 
99
        for (i = 0; i < 8; i++) {
100
                if (name[i] == '\0')
101
                        break;
102
                dcss_name[i] = toupper(name[i]);
103
        };
104
        for (; i < 8; i++)
105
                dcss_name[i] = ' ';
106
        ASCEBC(dcss_name, 8);
107
}
108
 
109
 
110
/*
111
 * search all segments in dcss_list, and return the one
112
 * namend *name. If not found, return NULL.
113
 */
114
static struct dcss_segment *
115
segment_by_name (char *name)
116
{
117
        char dcss_name[9];
118
        struct list_head *l;
119
        struct dcss_segment *tmp, *retval = NULL;
120
 
121
        BUG_ON(!mutex_is_locked(&dcss_lock));
122
        dcss_mkname (name, dcss_name);
123
        list_for_each (l, &dcss_list) {
124
                tmp = list_entry (l, struct dcss_segment, list);
125
                if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) {
126
                        retval = tmp;
127
                        break;
128
                }
129
        }
130
        return retval;
131
}
132
 
133
 
134
/*
135
 * Perform a function on a dcss segment.
136
 */
137
static inline int
138
dcss_diag (__u8 func, void *parameter,
139
           unsigned long *ret1, unsigned long *ret2)
140
{
141
        unsigned long rx, ry;
142
        int rc;
143
 
144
        rx = (unsigned long) parameter;
145
        ry = (unsigned long) func;
146
        asm volatile(
147
#ifdef CONFIG_64BIT
148
                "       sam31\n"
149
                "       diag    %0,%1,0x64\n"
150
                "       sam64\n"
151
#else
152
                "       diag    %0,%1,0x64\n"
153
#endif
154
                "       ipm     %2\n"
155
                "       srl     %2,28\n"
156
                : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
157
        *ret1 = rx;
158
        *ret2 = ry;
159
        return rc;
160
}
161
 
162
static inline int
163
dcss_diag_translate_rc (int vm_rc) {
164
        if (vm_rc == 44)
165
                return -ENOENT;
166
        return -EIO;
167
}
168
 
169
 
170
/* do a diag to get info about a segment.
171
 * fills start_address, end and vm_segtype fields
172
 */
173
static int
174
query_segment_type (struct dcss_segment *seg)
175
{
176
        struct qin64  *qin = kmalloc (sizeof(struct qin64), GFP_DMA);
177
        struct qout64 *qout = kmalloc (sizeof(struct qout64), GFP_DMA);
178
 
179
        int diag_cc, rc, i;
180
        unsigned long dummy, vmrc;
181
 
182
        if ((qin == NULL) || (qout == NULL)) {
183
                rc = -ENOMEM;
184
                goto out_free;
185
        }
186
 
187
        /* initialize diag input parameters */
188
        qin->qopcode = DCSS_FINDSEGA;
189
        qin->qoutptr = (unsigned long) qout;
190
        qin->qoutlen = sizeof(struct qout64);
191
        memcpy (qin->qname, seg->dcss_name, 8);
192
 
193
        diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc);
194
 
195
        if (diag_cc > 1) {
196
                PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc);
197
                rc = dcss_diag_translate_rc (vmrc);
198
                goto out_free;
199
        }
200
 
201
        if (qout->segcnt > 6) {
202
                rc = -ENOTSUPP;
203
                goto out_free;
204
        }
205
 
206
        if (qout->segcnt == 1) {
207
                seg->vm_segtype = qout->range[0].start & 0xff;
208
        } else {
209
                /* multi-part segment. only one type supported here:
210
                    - all parts are contiguous
211
                    - all parts are either EW or EN type
212
                    - maximum 6 parts allowed */
213
                unsigned long start = qout->segstart >> PAGE_SHIFT;
214
                for (i=0; i<qout->segcnt; i++) {
215
                        if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) &&
216
                            ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) {
217
                                rc = -ENOTSUPP;
218
                                goto out_free;
219
                        }
220
                        if (start != qout->range[i].start >> PAGE_SHIFT) {
221
                                rc = -ENOTSUPP;
222
                                goto out_free;
223
                        }
224
                        start = (qout->range[i].end >> PAGE_SHIFT) + 1;
225
                }
226
                seg->vm_segtype = SEG_TYPE_EWEN;
227
        }
228
 
229
        /* analyze diag output and update seg */
230
        seg->start_addr = qout->segstart;
231
        seg->end = qout->segend;
232
 
233
        memcpy (seg->range, qout->range, 6*sizeof(struct qrange));
234
        seg->segcnt = qout->segcnt;
235
 
236
        rc = 0;
237
 
238
 out_free:
239
        kfree(qin);
240
        kfree(qout);
241
        return rc;
242
}
243
 
244
/*
245
 * get info about a segment
246
 * possible return values:
247
 * -ENOSYS  : we are not running on VM
248
 * -EIO     : could not perform query diagnose
249
 * -ENOENT  : no such segment
250
 * -ENOTSUPP: multi-part segment cannot be used with linux
251
 * -ENOSPC  : segment cannot be used (overlaps with storage)
252
 * -ENOMEM  : out of memory
253
 * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
254
 */
255
int
256
segment_type (char* name)
257
{
258
        int rc;
259
        struct dcss_segment seg;
260
 
261
        if (!MACHINE_IS_VM)
262
                return -ENOSYS;
263
 
264
        dcss_mkname(name, seg.dcss_name);
265
        rc = query_segment_type (&seg);
266
        if (rc < 0)
267
                return rc;
268
        return seg.vm_segtype;
269
}
270
 
271
/*
272
 * real segment loading function, called from segment_load
273
 */
274
static int
275
__segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end)
276
{
277
        struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment),
278
                        GFP_DMA);
279
        int dcss_command, rc, diag_cc;
280
 
281
        if (seg == NULL) {
282
                rc = -ENOMEM;
283
                goto out;
284
        }
285
        dcss_mkname (name, seg->dcss_name);
286
        rc = query_segment_type (seg);
287
        if (rc < 0)
288
                goto out_free;
289
 
290
        rc = add_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
291
 
292
        switch (rc) {
293
        case 0:
294
                break;
295
        case -ENOSPC:
296
                PRINT_WARN("segment_load: not loading segment %s - overlaps "
297
                           "storage/segment\n", name);
298
                goto out_free;
299
        case -ERANGE:
300
                PRINT_WARN("segment_load: not loading segment %s - exceeds "
301
                           "kernel mapping range\n", name);
302
                goto out_free;
303
        default:
304
                PRINT_WARN("segment_load: not loading segment %s (rc: %d)\n",
305
                           name, rc);
306
                goto out_free;
307
        }
308
 
309
        seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL);
310
        if (seg->res == NULL) {
311
                rc = -ENOMEM;
312
                goto out_shared;
313
        }
314
        seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
315
        seg->res->start = seg->start_addr;
316
        seg->res->end = seg->end;
317
        memcpy(&seg->res_name, seg->dcss_name, 8);
318
        EBCASC(seg->res_name, 8);
319
        seg->res_name[8] = '\0';
320
        strncat(seg->res_name, " (DCSS)", 7);
321
        seg->res->name = seg->res_name;
322
        rc = seg->vm_segtype;
323
        if (rc == SEG_TYPE_SC ||
324
            ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared))
325
                seg->res->flags |= IORESOURCE_READONLY;
326
        if (request_resource(&iomem_resource, seg->res)) {
327
                rc = -EBUSY;
328
                kfree(seg->res);
329
                goto out_shared;
330
        }
331
 
332
        if (do_nonshared)
333
                dcss_command = DCSS_LOADNSR;
334
        else
335
                dcss_command = DCSS_LOADNOLY;
336
 
337
        diag_cc = dcss_diag(dcss_command, seg->dcss_name,
338
                        &seg->start_addr, &seg->end);
339
        if (diag_cc > 1) {
340
                PRINT_WARN ("segment_load: could not load segment %s - "
341
                                "diag returned error (%ld)\n",name,seg->end);
342
                rc = dcss_diag_translate_rc (seg->end);
343
                dcss_diag(DCSS_PURGESEG, seg->dcss_name,
344
                                &seg->start_addr, &seg->end);
345
                goto out_resource;
346
        }
347
        seg->do_nonshared = do_nonshared;
348
        atomic_set(&seg->ref_count, 1);
349
        list_add(&seg->list, &dcss_list);
350
        *addr = seg->start_addr;
351
        *end  = seg->end;
352
        if (do_nonshared)
353
                PRINT_INFO ("segment_load: loaded segment %s range %p .. %p "
354
                                "type %s in non-shared mode\n", name,
355
                                (void*)seg->start_addr, (void*)seg->end,
356
                                segtype_string[seg->vm_segtype]);
357
        else {
358
                PRINT_INFO ("segment_load: loaded segment %s range %p .. %p "
359
                                "type %s in shared mode\n", name,
360
                                (void*)seg->start_addr, (void*)seg->end,
361
                                segtype_string[seg->vm_segtype]);
362
        }
363
        goto out;
364
 out_resource:
365
        release_resource(seg->res);
366
        kfree(seg->res);
367
 out_shared:
368
        remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
369
 out_free:
370
        kfree(seg);
371
 out:
372
        return rc;
373
}
374
 
375
/*
376
 * this function loads a DCSS segment
377
 * name         : name of the DCSS
378
 * do_nonshared : 0 indicates that the dcss should be shared with other linux images
379
 *                1 indicates that the dcss should be exclusive for this linux image
380
 * addr         : will be filled with start address of the segment
381
 * end          : will be filled with end address of the segment
382
 * return values:
383
 * -ENOSYS  : we are not running on VM
384
 * -EIO     : could not perform query or load diagnose
385
 * -ENOENT  : no such segment
386
 * -ENOTSUPP: multi-part segment cannot be used with linux
387
 * -ENOSPC  : segment cannot be used (overlaps with storage)
388
 * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
389
 * -ERANGE  : segment cannot be used (exceeds kernel mapping range)
390
 * -EPERM   : segment is currently loaded with incompatible permissions
391
 * -ENOMEM  : out of memory
392
 * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
393
 */
394
int
395
segment_load (char *name, int do_nonshared, unsigned long *addr,
396
                unsigned long *end)
397
{
398
        struct dcss_segment *seg;
399
        int rc;
400
 
401
        if (!MACHINE_IS_VM)
402
                return -ENOSYS;
403
 
404
        mutex_lock(&dcss_lock);
405
        seg = segment_by_name (name);
406
        if (seg == NULL)
407
                rc = __segment_load (name, do_nonshared, addr, end);
408
        else {
409
                if (do_nonshared == seg->do_nonshared) {
410
                        atomic_inc(&seg->ref_count);
411
                        *addr = seg->start_addr;
412
                        *end  = seg->end;
413
                        rc    = seg->vm_segtype;
414
                } else {
415
                        *addr = *end = 0;
416
                        rc    = -EPERM;
417
                }
418
        }
419
        mutex_unlock(&dcss_lock);
420
        return rc;
421
}
422
 
423
/*
424
 * this function modifies the shared state of a DCSS segment. note that
425
 * name         : name of the DCSS
426
 * do_nonshared : 0 indicates that the dcss should be shared with other linux images
427
 *                1 indicates that the dcss should be exclusive for this linux image
428
 * return values:
429
 * -EIO     : could not perform load diagnose (segment gone!)
430
 * -ENOENT  : no such segment (segment gone!)
431
 * -EAGAIN  : segment is in use by other exploiters, try later
432
 * -EINVAL  : no segment with the given name is currently loaded - name invalid
433
 * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
434
 * 0        : operation succeeded
435
 */
436
int
437
segment_modify_shared (char *name, int do_nonshared)
438
{
439
        struct dcss_segment *seg;
440
        unsigned long dummy;
441
        int dcss_command, rc, diag_cc;
442
 
443
        mutex_lock(&dcss_lock);
444
        seg = segment_by_name (name);
445
        if (seg == NULL) {
446
                rc = -EINVAL;
447
                goto out_unlock;
448
        }
449
        if (do_nonshared == seg->do_nonshared) {
450
                PRINT_INFO ("segment_modify_shared: not reloading segment %s"
451
                                " - already in requested mode\n",name);
452
                rc = 0;
453
                goto out_unlock;
454
        }
455
        if (atomic_read (&seg->ref_count) != 1) {
456
                PRINT_WARN ("segment_modify_shared: not reloading segment %s - "
457
                                "segment is in use by other driver(s)\n",name);
458
                rc = -EAGAIN;
459
                goto out_unlock;
460
        }
461
        release_resource(seg->res);
462
        if (do_nonshared) {
463
                dcss_command = DCSS_LOADNSR;
464
                seg->res->flags &= ~IORESOURCE_READONLY;
465
        } else {
466
                dcss_command = DCSS_LOADNOLY;
467
                if (seg->vm_segtype == SEG_TYPE_SR ||
468
                    seg->vm_segtype == SEG_TYPE_ER)
469
                        seg->res->flags |= IORESOURCE_READONLY;
470
        }
471
        if (request_resource(&iomem_resource, seg->res)) {
472
                PRINT_WARN("segment_modify_shared: could not reload segment %s"
473
                           " - overlapping resources\n", name);
474
                rc = -EBUSY;
475
                kfree(seg->res);
476
                goto out_del;
477
        }
478
        dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
479
        diag_cc = dcss_diag(dcss_command, seg->dcss_name,
480
                        &seg->start_addr, &seg->end);
481
        if (diag_cc > 1) {
482
                PRINT_WARN ("segment_modify_shared: could not reload segment %s"
483
                                " - diag returned error (%ld)\n",name,seg->end);
484
                rc = dcss_diag_translate_rc (seg->end);
485
                goto out_del;
486
        }
487
        seg->do_nonshared = do_nonshared;
488
        rc = 0;
489
        goto out_unlock;
490
 out_del:
491
        remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
492
        list_del(&seg->list);
493
        dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
494
        kfree(seg);
495
 out_unlock:
496
        mutex_unlock(&dcss_lock);
497
        return rc;
498
}
499
 
500
/*
501
 * Decrease the use count of a DCSS segment and remove
502
 * it from the address space if nobody is using it
503
 * any longer.
504
 */
505
void
506
segment_unload(char *name)
507
{
508
        unsigned long dummy;
509
        struct dcss_segment *seg;
510
 
511
        if (!MACHINE_IS_VM)
512
                return;
513
 
514
        mutex_lock(&dcss_lock);
515
        seg = segment_by_name (name);
516
        if (seg == NULL) {
517
                PRINT_ERR ("could not find segment %s in segment_unload, "
518
                                "please report to linux390@de.ibm.com\n",name);
519
                goto out_unlock;
520
        }
521
        if (atomic_dec_return(&seg->ref_count) != 0)
522
                goto out_unlock;
523
        release_resource(seg->res);
524
        kfree(seg->res);
525
        remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
526
        list_del(&seg->list);
527
        dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
528
        kfree(seg);
529
out_unlock:
530
        mutex_unlock(&dcss_lock);
531
}
532
 
533
/*
534
 * save segment content permanently
535
 */
536
void
537
segment_save(char *name)
538
{
539
        struct dcss_segment *seg;
540
        int startpfn = 0;
541
        int endpfn = 0;
542
        char cmd1[160];
543
        char cmd2[80];
544
        int i, response;
545
 
546
        if (!MACHINE_IS_VM)
547
                return;
548
 
549
        mutex_lock(&dcss_lock);
550
        seg = segment_by_name (name);
551
 
552
        if (seg == NULL) {
553
                PRINT_ERR("could not find segment %s in segment_save, please "
554
                          "report to linux390@de.ibm.com\n", name);
555
                goto out;
556
        }
557
 
558
        startpfn = seg->start_addr >> PAGE_SHIFT;
559
        endpfn = (seg->end) >> PAGE_SHIFT;
560
        sprintf(cmd1, "DEFSEG %s", name);
561
        for (i=0; i<seg->segcnt; i++) {
562
                sprintf(cmd1+strlen(cmd1), " %X-%X %s",
563
                        seg->range[i].start >> PAGE_SHIFT,
564
                        seg->range[i].end >> PAGE_SHIFT,
565
                        segtype_string[seg->range[i].start & 0xff]);
566
        }
567
        sprintf(cmd2, "SAVESEG %s", name);
568
        response = 0;
569
        cpcmd(cmd1, NULL, 0, &response);
570
        if (response) {
571
                PRINT_ERR("segment_save: DEFSEG failed with response code %i\n",
572
                          response);
573
                goto out;
574
        }
575
        cpcmd(cmd2, NULL, 0, &response);
576
        if (response) {
577
                PRINT_ERR("segment_save: SAVESEG failed with response code %i\n",
578
                          response);
579
                goto out;
580
        }
581
out:
582
        mutex_unlock(&dcss_lock);
583
}
584
 
585
EXPORT_SYMBOL(segment_load);
586
EXPORT_SYMBOL(segment_unload);
587
EXPORT_SYMBOL(segment_save);
588
EXPORT_SYMBOL(segment_type);
589
EXPORT_SYMBOL(segment_modify_shared);

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.