~ubuntu-branches/ubuntu/hardy/silo/hardy-updates

« back to all changes in this revision

Viewing changes to second/memory.c

  • Committer: Bazaar Package Importer
  • Author(s): Fabio M. Di Nitto
  • Date: 2007-10-25 09:28:08 UTC
  • mfrom: (15.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20071025092808-1yhj12t7s4zqsfu5
Tags: 1.4.13a+git20070930-1ubuntu1
* Merge from debian unstable, remaining changes:
  - Build with -fno-stack-protector.
  - Change silo.postinst to automatically update the boot block without
    invoking siloconfig and keep asking questions on upgrades.
  - Convert silo.conf to use /dev/disk/by-uuid.
  - Ubuntu maintainer foobar.
  - Fix debian/rules call to dh_installdocs.
  - Drop the requirement of gcc-4.1 and start using default gcc.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Memory utilities
 
2
 
 
3
   Copyright (C) 1995, 1996 David S. Miller
 
4
                 1996, 1998, 1999 Jakub Jelinek
 
5
                 1996 Andrew Tridgell
 
6
 
 
7
   This program is free software; you can redistribute it and/or modify
 
8
   it under the terms of the GNU General Public License as published by
 
9
   the Free Software Foundation; either version 2 of the License, or
 
10
   (at your option) any later version.
 
11
 
 
12
   This program is distributed in the hope that it will be useful,
 
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
   GNU General Public License for more details.
 
16
 
 
17
   You should have received a copy of the GNU General Public License
 
18
   along with this program; if not, write to the Free Software
 
19
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
 
20
   USA.  */
 
21
 
 
22
#include <silo.h>
 
23
 
 
24
#define INITRD_VIRT_ADDR        0x40c00000
 
25
#define IMAGE_VIRT_ADDR         0x40000000
 
26
 
 
27
extern int initrd_can_do_64bit_phys;
 
28
 
 
29
static char *sun4u_memory_find (unsigned int len, int is_kernel);
 
30
 
 
31
struct linux_prom_registers prom_reg_memlist[64];
 
32
struct linux_mlist_v0 prom_phys_avail[64];
 
33
 
 
34
/* Internal Prom library routine to sort a linux_mlist_v0 memory
 
35
 * list.  Used below in initialization.
 
36
 */
 
37
static void prom_sortmemlist (struct linux_mlist_v0 *thislist)
 
38
{
 
39
    int swapi = 0;
 
40
    int i, mitr, tmpsize;
 
41
    char *tmpaddr;
 
42
    char *lowest;
 
43
 
 
44
    for (i = 0; thislist[i].theres_more != 0; i++) {
 
45
        lowest = thislist[i].start_adr;
 
46
        for (mitr = i + 1; thislist[mitr - 1].theres_more != 0; mitr++)
 
47
            if (thislist[mitr].start_adr < lowest) {
 
48
                lowest = thislist[mitr].start_adr;
 
49
                swapi = mitr;
 
50
            }
 
51
        if (lowest == thislist[i].start_adr)
 
52
            continue;
 
53
        tmpaddr = thislist[swapi].start_adr;
 
54
        tmpsize = thislist[swapi].num_bytes;
 
55
        for (mitr = swapi; mitr > i; mitr--) {
 
56
            thislist[mitr].start_adr = thislist[mitr - 1].start_adr;
 
57
            thislist[mitr].num_bytes = thislist[mitr - 1].num_bytes;
 
58
        }
 
59
        thislist[i].start_adr = tmpaddr;
 
60
        thislist[i].num_bytes = tmpsize;
 
61
    }
 
62
}
 
63
 
 
64
/* Initialize the memory lists based upon the prom version. */
 
65
struct linux_mlist_v0 *prom_meminit (void)
 
66
{
 
67
    int node = 0;
 
68
    unsigned int iter, num_regs;
 
69
    struct linux_mlist_v0 *mptr;        /* ptr for traversal */
 
70
    static int meminited = 0;
 
71
    
 
72
    if (meminited) return prom_phys_avail;
 
73
    meminited = 1;
 
74
 
 
75
    switch (prom_vers) {
 
76
    case PROM_V0:
 
77
        /* Nice, kind of easier to do in this case. */
 
78
        /* First, the total physical descriptors. */
 
79
        /* Last, the available physical descriptors. */
 
80
        for (mptr = (*(romvec->pv_v0mem.v0_available)), iter = 0;
 
81
             mptr; mptr = mptr->theres_more, iter++) {
 
82
            prom_phys_avail[iter].start_adr = mptr->start_adr;
 
83
            prom_phys_avail[iter].num_bytes = mptr->num_bytes;
 
84
            prom_phys_avail[iter].theres_more = &prom_phys_avail[iter + 1];
 
85
        }
 
86
        prom_phys_avail[iter - 1].theres_more = 0;
 
87
        prom_sortmemlist (prom_phys_avail);
 
88
        break;
 
89
    case PROM_V2:
 
90
    case PROM_V3:
 
91
        /* Grrr, have to traverse the prom device tree ;( */
 
92
        node = prom_getchild (prom_root_node);
 
93
        node = prom_searchsiblings (node, "memory");
 
94
        num_regs = prom_getproperty (node, "available",
 
95
                                     (char *) prom_reg_memlist,
 
96
                                     sizeof (prom_reg_memlist));
 
97
        num_regs = (num_regs / sizeof (struct linux_prom_registers));
 
98
        for (iter = 0; iter < num_regs; iter++) {
 
99
            prom_phys_avail[iter].start_adr =
 
100
                prom_reg_memlist[iter].phys_addr;
 
101
            prom_phys_avail[iter].num_bytes =
 
102
                (unsigned long) prom_reg_memlist[iter].reg_size;
 
103
            prom_phys_avail[iter].theres_more =
 
104
                &prom_phys_avail[iter + 1];
 
105
        }
 
106
        prom_phys_avail[iter - 1].theres_more = 0;
 
107
        prom_sortmemlist (prom_phys_avail);
 
108
    default:
 
109
        break;
 
110
 
 
111
    }
 
112
    return prom_phys_avail;
 
113
}
 
114
 
 
115
static int sun4c_hwflushes;
 
116
static int sun4c_linesize;
 
117
static void sun4c_init (void)
 
118
{
 
119
    static int inited = 0;
 
120
    int propval;
 
121
    
 
122
    if (!inited) {
 
123
        inited = 1;
 
124
        propval = prom_getintdefault (prom_root_node, "vac_hwflush", -1);
 
125
        sun4c_hwflushes = (propval == -1) ? prom_getintdefault (prom_root_node, "vac-hwflush", 0) : propval;
 
126
        sun4c_linesize = prom_getintdefault (prom_root_node, "vac-linesize", 16);
 
127
    }
 
128
}
 
129
 
 
130
void sun4c_map (unsigned long virtual, unsigned long page)
 
131
{
 
132
    unsigned long virt = virtual;
 
133
 
 
134
    sun4c_init ();
 
135
    if (sun4c_hwflushes) {
 
136
        __asm__ __volatile__ ("\n\tsta %%g0, [%0] 0x06\n\t" : : "r" (virt));
 
137
    } else {
 
138
        unsigned long end = virt + 4096;
 
139
        
 
140
        for (; virt < end; virt += sun4c_linesize)
 
141
            __asm__ __volatile__ ("\n\tsta %%g0, [%0] 0x0d\n\t" : : "r" (virt));
 
142
    }
 
143
    virt = virtual;
 
144
    __asm__ __volatile__ ("\n\tsta %1, [%0] 0x04\n\t" : : "r" (virt), "r" (page));
 
145
}
 
146
 
 
147
int sun4c_mapio (unsigned long phys, unsigned long virtual, int rdonly)
 
148
{
 
149
    unsigned long page = ((phys >> 12) & 0xffff) | 0x94000000;
 
150
    
 
151
    if (!rdonly) page |= 0x40000000;
 
152
    sun4c_map (virtual & ~4095, page);
 
153
    return 0;
 
154
}
 
155
 
 
156
void sun4c_unmapio (unsigned long virtual)
 
157
{
 
158
    sun4c_map (virtual & ~4095, 0);
 
159
}
 
160
 
 
161
inline unsigned long sun4m_get_lev1 (void)
 
162
{
 
163
    unsigned long ret;
 
164
    
 
165
    __asm__ ("\n\t"
 
166
        "set 0x100, %0\n\t"
 
167
        "lda [%0] 4, %0\n\t"
 
168
        "sll %0, 4, %0\n\t"
 
169
        "lda [%0] 32, %0\n\t"
 
170
        "srl %0, 4, %0\n\t"
 
171
        "sll %0, 8, %0\n\t" : "=r" (ret));
 
172
    return ret;
 
173
}
 
174
 
 
175
inline unsigned long sun4m_probe (unsigned long l)
 
176
{
 
177
    unsigned long ret;
 
178
    
 
179
    __asm__ ("\n\t"
 
180
        "lda [%1] 3, %0" : "=r" (ret) : "r" (l | 0x400));
 
181
    return ret;
 
182
}
 
183
 
 
184
inline unsigned long sun4m_get_direct (unsigned long l)
 
185
{
 
186
    unsigned long ret;
 
187
    __asm__ ("\n\t"
 
188
        "lda [%1] 32, %0\n\t" : "=r" (ret) : "r" (l));
 
189
    return ret;
 
190
}
 
191
 
 
192
inline void sun4m_set_direct (unsigned long l, unsigned long set)
 
193
{
 
194
    __asm__ ("\n\t"
 
195
        "sta %0, [%1] 32\n\t" : : "r" (set), "r" (l));
 
196
}
 
197
 
 
198
unsigned long long initrd_phys;
 
199
 
 
200
unsigned long sun4m_initrd_pa;
 
201
unsigned long sun4m_initrd_va;
 
202
 
 
203
char *memory_find (int len)
 
204
{
 
205
    register struct linux_mlist_v0 *mlist;
 
206
    char *beg = 0, *start;
 
207
    int l = 0, num;
 
208
    unsigned long totalmem = 0;
 
209
    char *min = (char *)0x300000;
 
210
 
 
211
    if (architecture != sun4u) {
 
212
        prom_meminit ();
 
213
        for (mlist = prom_phys_avail; mlist; mlist = mlist->theres_more) {
 
214
            totalmem += mlist->num_bytes;
 
215
            if (totalmem >= 0x4000000)
 
216
                break;
 
217
        }
 
218
        if (architecture != sun4c) {
 
219
            unsigned long ll;
 
220
            if (totalmem >= 0x4000000)
 
221
                min = (char *)0x3000000;
 
222
            else if (totalmem >= 0x2000000)
 
223
                min = (char *)0x1000000;
 
224
            ll = (sun4m_probe (0x4000) & 0xffffff00) << 4;
 
225
            ll -= 0x4000;
 
226
            min += ll;
 
227
        }
 
228
        mlist = prom_phys_avail;
 
229
        for (;;) {
 
230
            if (beg && mlist->start_adr != beg + l)
 
231
                beg = 0;
 
232
            start = mlist->start_adr;
 
233
            num = mlist->num_bytes;
 
234
            if (start <= min) {
 
235
                num += start - min;
 
236
                start = min;
 
237
            }
 
238
            if (num > 0) {
 
239
                if (num + (beg ? l : 0) >= len) {
 
240
                    if (!beg) beg = start;
 
241
                    if (architecture == sun4c)
 
242
                        return beg;
 
243
                    else {
 
244
                        unsigned long lev1;
 
245
                        int i;
 
246
                        sun4m_initrd_pa = (unsigned long)beg;
 
247
                        initrd_phys = (unsigned long long)(unsigned long)beg;
 
248
                        lev1 = sun4m_get_lev1();
 
249
                        for (i = 0x60; i < 0xa0; i++)
 
250
                            if (!(sun4m_get_direct(lev1 + 4*i) & 3))
 
251
                                break;
 
252
                        if (i == 0xa0) return (char *)0;
 
253
                        sun4m_set_direct(lev1 + 4*i, ((sun4m_initrd_pa & 0xff000000) >> 4) | 0x9e);
 
254
                        sun4m_initrd_va = i << 24;
 
255
                        return (char *)sun4m_initrd_va + (sun4m_initrd_pa & 0xffffff);
 
256
                    }
 
257
                }
 
258
                if (beg) l += num;
 
259
                else {
 
260
                    beg = start;
 
261
                    l = num;
 
262
                }
 
263
            }
 
264
            if (!mlist->theres_more) goto not_found;
 
265
            mlist = mlist->theres_more;
 
266
        }
 
267
    } else {
 
268
        return sun4u_memory_find((len + 0x1fff) & ~0x2000, 0);
 
269
    }
 
270
not_found:
 
271
    return (char *)0;
 
272
}
 
273
 
 
274
static unsigned long long sun4u_image_virt, sun4u_image_len, sun4u_image_phys;
 
275
static unsigned long long sun4u_initrd_virt, sun4u_initrd_len;
 
276
unsigned long long sun4u_initrd_phys;
 
277
 
 
278
static char *sun4u_memory_find (unsigned int len, int is_kernel)
 
279
{
 
280
        int n, node, i;
 
281
        struct p1275_mem {
 
282
                unsigned long long phys;
 
283
                unsigned long long size;
 
284
        } *p = (struct p1275_mem *)0;
 
285
        unsigned int virt = (is_kernel ? IMAGE_VIRT_ADDR : INITRD_VIRT_ADDR);
 
286
        unsigned long long phys = 0, phys_base;
 
287
 
 
288
        p = (struct p1275_mem *)malloc(2048);
 
289
 
 
290
        node = prom_finddevice("/memory");
 
291
 
 
292
        n = prom_getproplen(node, "available");
 
293
 
 
294
        if (!n || n == -1 || prom_getproperty(node, "available", (char *)p, 2048) == -1) {
 
295
                free (p);
 
296
                printf("Could not get available property\n");
 
297
                return (char *)0;
 
298
        }
 
299
 
 
300
        phys = 0;
 
301
        n /= sizeof(*p);
 
302
 
 
303
        phys_base = ~(unsigned long long)0;
 
304
        for (i = 0; i < n; i++) {
 
305
                if (p[i].phys < phys_base)
 
306
                        phys_base = p[i].phys;
 
307
        }
 
308
 
 
309
        for (i = 0; i < n; i++) {
 
310
                /* Do not mess with first 4 Megs of memory */
 
311
                if (p[i].phys == phys_base) {
 
312
                        if (p[i].size <= 0x400000)
 
313
                                continue;
 
314
                        p[i].phys += 0x400000;
 
315
                        p[i].size -= 0x400000;
 
316
                }
 
317
 
 
318
                /* Make sure initrd doesn't overwrite kernel */
 
319
                if (!is_kernel && p[i].phys == sun4u_image_phys) {
 
320
                        if (p[i].size <= sun4u_image_len)
 
321
                                continue;
 
322
                        p[i].phys += sun4u_image_len;
 
323
                        p[i].size -= sun4u_image_len;
 
324
                }
 
325
 
 
326
                /* Make sure initrd phys isn't greater than 32-bits. We
 
327
                 * can only pass unsigned int to the kernel for this
 
328
                 * location. */
 
329
                if (!is_kernel && !initrd_can_do_64bit_phys && p[i].phys >= 0x0000000100000000ULL)
 
330
                        continue;
 
331
 
 
332
                if (p[i].size >= len) {
 
333
                        phys = p[i].phys;
 
334
                        break;
 
335
                }
 
336
        }
 
337
 
 
338
        free (p);
 
339
 
 
340
        if (!phys) {
 
341
                printf("Could not find any available memory\n");
 
342
                return (char *)0;
 
343
        }
 
344
 
 
345
        if (prom_map(PROM_MAP_DEFAULT, (unsigned long long)len, virt, phys) == -1) {
 
346
                printf("Could not map memory\n");
 
347
                return (char *)0;
 
348
        }
 
349
 
 
350
        if (is_kernel) {
 
351
                sun4u_image_len = len;
 
352
                sun4u_image_virt = virt;
 
353
                sun4u_image_phys = phys;
 
354
                phys += 0x4000ULL;
 
355
                virt += 0x4000;
 
356
        } else {
 
357
                sun4u_initrd_len = len;
 
358
                sun4u_initrd_virt = virt;
 
359
                initrd_phys = phys;
 
360
                /* Not sure what the old kernel crap is for, but it
 
361
                 * expects the passed initrd physical to be relative to
 
362
                 * the phys memory base. We'll keep compatible with older
 
363
                 * kernels to avoid any problems. */
 
364
                sun4u_initrd_phys = phys - phys_base;
 
365
        }
 
366
 
 
367
        return (char *)virt;
 
368
}
 
369
 
 
370
static void sun4u_memory_release(int is_kernel)
 
371
{
 
372
        unsigned long long virt, len;
 
373
 
 
374
        if (is_kernel) {
 
375
                virt = sun4u_image_virt;
 
376
                len = sun4u_image_len;
 
377
        } else {
 
378
                virt = sun4u_initrd_virt;
 
379
                len = sun4u_initrd_len;
 
380
        }
 
381
 
 
382
        if (!len)
 
383
                return;
 
384
 
 
385
 
 
386
        prom_unmap(len, virt);
 
387
 
 
388
        if (is_kernel)
 
389
                sun4u_image_len = 0;
 
390
        else
 
391
                sun4u_initrd_len = 0;
 
392
}
 
393
 
 
394
char *image_memory_find (unsigned int len)
 
395
{
 
396
        /* This only works for sparc64 */
 
397
        if (architecture != sun4u)
 
398
                return (char *)0;
 
399
 
 
400
        return sun4u_memory_find(len, 1);
 
401
}
 
402
 
 
403
void image_memory_release(void)
 
404
{
 
405
        if (architecture != sun4u)
 
406
                return;
 
407
 
 
408
        sun4u_memory_release(1);
 
409
}
 
410
 
 
411
void memory_release(void)
 
412
{
 
413
    if (architecture == sun4u) {
 
414
            sun4u_memory_release(0);
 
415
    } else if (sun4m_initrd_pa) {
 
416
        unsigned long lev1;
 
417
        lev1 = sun4m_get_lev1();
 
418
        sun4m_set_direct(lev1 + (sun4m_initrd_va >> 24) * 4, 0);
 
419
    }
 
420
}