Annotation of qemu/hw/arm_gic.c, revision 1.1

1.1     ! root        1: /* 
        !             2:  * ARM AMBA Generic/Distributed Interrupt Controller
        !             3:  *
        !             4:  * Copyright (c) 2006 CodeSourcery.
        !             5:  * Written by Paul Brook
        !             6:  *
        !             7:  * This code is licenced under the GPL.
        !             8:  */
        !             9: 
        !            10: /* TODO: Some variants of this controller can handle multiple CPUs.
        !            11:    Currently only single CPU operation is implemented.  */
        !            12: 
        !            13: #include "vl.h"
        !            14: #include "arm_pic.h"
        !            15: 
        !            16: //#define DEBUG_GIC
        !            17: 
        !            18: #ifdef DEBUG_GIC
        !            19: #define DPRINTF(fmt, args...) \
        !            20: do { printf("arm_gic: " fmt , ##args); } while (0)
        !            21: #else
        !            22: #define DPRINTF(fmt, args...) do {} while(0)
        !            23: #endif
        !            24: 
        !            25: /* Distributed interrupt controller.  */
        !            26: 
        !            27: static const uint8_t gic_id[] =
        !            28: { 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
        !            29: 
        !            30: #define GIC_NIRQ 96
        !            31: 
        !            32: typedef struct gic_irq_state
        !            33: {
        !            34:     unsigned enabled:1;
        !            35:     unsigned pending:1;
        !            36:     unsigned active:1;
        !            37:     unsigned level:1;
        !            38:     unsigned model:1; /* 0 = 1:N, 1 = N:N */
        !            39:     unsigned trigger:1; /* nonzero = edge triggered.  */
        !            40: } gic_irq_state;
        !            41: 
        !            42: #define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
        !            43: #define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
        !            44: #define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
        !            45: #define GIC_SET_PENDING(irq) s->irq_state[irq].pending = 1
        !            46: #define GIC_CLEAR_PENDING(irq) s->irq_state[irq].pending = 0
        !            47: #define GIC_TEST_PENDING(irq) s->irq_state[irq].pending
        !            48: #define GIC_SET_ACTIVE(irq) s->irq_state[irq].active = 1
        !            49: #define GIC_CLEAR_ACTIVE(irq) s->irq_state[irq].active = 0
        !            50: #define GIC_TEST_ACTIVE(irq) s->irq_state[irq].active
        !            51: #define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
        !            52: #define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
        !            53: #define GIC_TEST_MODEL(irq) s->irq_state[irq].model
        !            54: #define GIC_SET_LEVEL(irq) s->irq_state[irq].level = 1
        !            55: #define GIC_CLEAR_LEVEL(irq) s->irq_state[irq].level = 0
        !            56: #define GIC_TEST_LEVEL(irq) s->irq_state[irq].level
        !            57: #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
        !            58: #define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
        !            59: #define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
        !            60: 
        !            61: typedef struct gic_state
        !            62: {
        !            63:     arm_pic_handler handler;
        !            64:     uint32_t base;
        !            65:     void *parent;
        !            66:     int parent_irq;
        !            67:     int enabled;
        !            68:     int cpu_enabled;
        !            69: 
        !            70:     gic_irq_state irq_state[GIC_NIRQ];
        !            71:     int irq_target[GIC_NIRQ];
        !            72:     int priority[GIC_NIRQ];
        !            73:     int last_active[GIC_NIRQ];
        !            74: 
        !            75:     int priority_mask;
        !            76:     int running_irq;
        !            77:     int running_priority;
        !            78:     int current_pending;
        !            79: } gic_state;
        !            80: 
        !            81: /* TODO: Many places that call this routine could be optimized.  */
        !            82: /* Update interrupt status after enabled or pending bits have been changed.  */
        !            83: static void gic_update(gic_state *s)
        !            84: {
        !            85:     int best_irq;
        !            86:     int best_prio;
        !            87:     int irq;
        !            88: 
        !            89:     s->current_pending = 1023;
        !            90:     if (!s->enabled || !s->cpu_enabled) {
        !            91:         pic_set_irq_new(s->parent, s->parent_irq, 0);
        !            92:         return;
        !            93:     }
        !            94:     best_prio = 0x100;
        !            95:     best_irq = 1023;
        !            96:     for (irq = 0; irq < 96; irq++) {
        !            97:         if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq)) {
        !            98:             if (s->priority[irq] < best_prio) {
        !            99:                 best_prio = s->priority[irq];
        !           100:                 best_irq = irq;
        !           101:             }
        !           102:         }
        !           103:     }
        !           104:     if (best_prio > s->priority_mask) {
        !           105:         pic_set_irq_new(s->parent, s->parent_irq, 0);
        !           106:     } else {
        !           107:         s->current_pending = best_irq;
        !           108:         if (best_prio < s->running_priority) {
        !           109:             DPRINTF("Raised pending IRQ %d\n", best_irq);
        !           110:             pic_set_irq_new(s->parent, s->parent_irq, 1);
        !           111:         }
        !           112:     }
        !           113: }
        !           114: 
        !           115: static void gic_set_irq(void *opaque, int irq, int level)
        !           116: {
        !           117:     gic_state *s = (gic_state *)opaque;
        !           118:     /* The first external input line is internal interrupt 32.  */
        !           119:     irq += 32;
        !           120:     if (level == GIC_TEST_LEVEL(irq)) 
        !           121:         return;
        !           122: 
        !           123:     if (level) {
        !           124:         GIC_SET_LEVEL(irq);
        !           125:         if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
        !           126:             DPRINTF("Set %d pending\n", irq);
        !           127:             GIC_SET_PENDING(irq);
        !           128:         }
        !           129:     } else {
        !           130:         GIC_CLEAR_LEVEL(irq);
        !           131:     }
        !           132:     gic_update(s);
        !           133: }
        !           134: 
        !           135: static void gic_set_running_irq(gic_state *s, int irq)
        !           136: {
        !           137:     s->running_irq = irq;
        !           138:     if (irq == 1023)
        !           139:         s->running_priority = 0x100;
        !           140:     else
        !           141:         s->running_priority = s->priority[irq];
        !           142:     gic_update(s);
        !           143: }
        !           144: 
        !           145: static uint32_t gic_acknowledge_irq(gic_state *s)
        !           146: {
        !           147:     int new_irq;
        !           148:     new_irq = s->current_pending;
        !           149:     if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) {
        !           150:         DPRINTF("ACK no pending IRQ\n");
        !           151:         return 1023;
        !           152:     }
        !           153:     pic_set_irq_new(s->parent, s->parent_irq, 0);
        !           154:     s->last_active[new_irq] = s->running_irq;
        !           155:     /* For level triggered interrupts we clear the pending bit while
        !           156:        the interrupt is active.  */
        !           157:     GIC_CLEAR_PENDING(new_irq);
        !           158:     gic_set_running_irq(s, new_irq);
        !           159:     DPRINTF("ACK %d\n", new_irq);
        !           160:     return new_irq;
        !           161: }
        !           162: 
        !           163: static void gic_complete_irq(gic_state * s, int irq)
        !           164: {
        !           165:     int update = 0;
        !           166:     DPRINTF("EOI %d\n", irq);
        !           167:     if (s->running_irq == 1023)
        !           168:         return; /* No active IRQ.  */
        !           169:     if (irq != 1023) {
        !           170:         /* Mark level triggered interrupts as pending if they are still
        !           171:            raised.  */
        !           172:         if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
        !           173:                 && GIC_TEST_LEVEL(irq)) {
        !           174:             GIC_SET_PENDING(irq);
        !           175:             update = 1;
        !           176:         }
        !           177:     }
        !           178:     if (irq != s->running_irq) {
        !           179:         /* Complete an IRQ that is not currently running.  */
        !           180:         int tmp = s->running_irq;
        !           181:         while (s->last_active[tmp] != 1023) {
        !           182:             if (s->last_active[tmp] == irq) {
        !           183:                 s->last_active[tmp] = s->last_active[irq];
        !           184:                 break;
        !           185:             }
        !           186:             tmp = s->last_active[tmp];
        !           187:         }
        !           188:         if (update) {
        !           189:             gic_update(s);
        !           190:         }
        !           191:     } else {
        !           192:         /* Complete the current running IRQ.  */
        !           193:         gic_set_running_irq(s, s->last_active[s->running_irq]);
        !           194:     }
        !           195: }
        !           196: 
        !           197: static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
        !           198: {
        !           199:     gic_state *s = (gic_state *)opaque;
        !           200:     uint32_t res;
        !           201:     int irq;
        !           202:     int i;
        !           203: 
        !           204:     offset -= s->base + 0x1000;
        !           205:     if (offset < 0x100) {
        !           206:         if (offset == 0)
        !           207:             return s->enabled;
        !           208:         if (offset == 4)
        !           209:             return (GIC_NIRQ / 32) - 1;
        !           210:         if (offset < 0x08)
        !           211:             return 0;
        !           212:         goto bad_reg;
        !           213:     } else if (offset < 0x200) {
        !           214:         /* Interrupt Set/Clear Enable.  */
        !           215:         if (offset < 0x180)
        !           216:             irq = (offset - 0x100) * 8;
        !           217:         else
        !           218:             irq = (offset - 0x180) * 8;
        !           219:         if (irq >= GIC_NIRQ)
        !           220:             goto bad_reg;
        !           221:         res = 0;
        !           222:         for (i = 0; i < 8; i++) {
        !           223:             if (GIC_TEST_ENABLED(irq + i)) {
        !           224:                 res |= (1 << i);
        !           225:             }
        !           226:         }
        !           227:     } else if (offset < 0x300) {
        !           228:         /* Interrupt Set/Clear Pending.  */
        !           229:         if (offset < 0x280)
        !           230:             irq = (offset - 0x200) * 8;
        !           231:         else
        !           232:             irq = (offset - 0x280) * 8;
        !           233:         if (irq >= GIC_NIRQ)
        !           234:             goto bad_reg;
        !           235:         res = 0;
        !           236:         for (i = 0; i < 8; i++) {
        !           237:             if (GIC_TEST_PENDING(irq + i)) {
        !           238:                 res |= (1 << i);
        !           239:             }
        !           240:         }
        !           241:     } else if (offset < 0x400) {
        !           242:         /* Interrupt Active.  */
        !           243:         irq = (offset - 0x300) * 8;
        !           244:         if (irq >= GIC_NIRQ)
        !           245:             goto bad_reg;
        !           246:         res = 0;
        !           247:         for (i = 0; i < 8; i++) {
        !           248:             if (GIC_TEST_ACTIVE(irq + i)) {
        !           249:                 res |= (1 << i);
        !           250:             }
        !           251:         }
        !           252:     } else if (offset < 0x800) {
        !           253:         /* Interrupt Priority.  */
        !           254:         irq = offset - 0x400;
        !           255:         if (irq >= GIC_NIRQ)
        !           256:             goto bad_reg;
        !           257:         res = s->priority[irq];
        !           258:     } else if (offset < 0xc00) {
        !           259:         /* Interrupt CPU Target.  */
        !           260:         irq = offset - 0x800;
        !           261:         if (irq >= GIC_NIRQ)
        !           262:             goto bad_reg;
        !           263:         res = s->irq_target[irq];
        !           264:     } else if (offset < 0xf00) {
        !           265:         /* Interrupt Configuration.  */
        !           266:         irq = (offset - 0xc00) * 2;
        !           267:         if (irq >= GIC_NIRQ)
        !           268:             goto bad_reg;
        !           269:         res = 0;
        !           270:         for (i = 0; i < 4; i++) {
        !           271:             if (GIC_TEST_MODEL(irq + i))
        !           272:                 res |= (1 << (i * 2));
        !           273:             if (GIC_TEST_TRIGGER(irq + i))
        !           274:                 res |= (2 << (i * 2));
        !           275:         }
        !           276:     } else if (offset < 0xfe0) {
        !           277:         goto bad_reg;
        !           278:     } else /* offset >= 0xfe0 */ {
        !           279:         if (offset & 3) {
        !           280:             res = 0;
        !           281:         } else {
        !           282:             res = gic_id[(offset - 0xfe0) >> 2];
        !           283:         }
        !           284:     }
        !           285:     return res;
        !           286: bad_reg:
        !           287:     cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset);
        !           288:     return 0;
        !           289: }
        !           290: 
        !           291: static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
        !           292: {
        !           293:     uint32_t val;
        !           294:     val = gic_dist_readb(opaque, offset);
        !           295:     val |= gic_dist_readb(opaque, offset + 1) << 8;
        !           296:     return val;
        !           297: }
        !           298: 
        !           299: static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
        !           300: {
        !           301:     uint32_t val;
        !           302:     val = gic_dist_readw(opaque, offset);
        !           303:     val |= gic_dist_readw(opaque, offset + 2) << 16;
        !           304:     return val;
        !           305: }
        !           306: 
        !           307: static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
        !           308:                             uint32_t value)
        !           309: {
        !           310:     gic_state *s = (gic_state *)opaque;
        !           311:     int irq;
        !           312:     int i;
        !           313: 
        !           314:     offset -= s->base + 0x1000;
        !           315:     if (offset < 0x100) {
        !           316:         if (offset == 0) {
        !           317:             s->enabled = (value & 1);
        !           318:             DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
        !           319:         } else if (offset < 4) {
        !           320:             /* ignored.  */
        !           321:         } else {
        !           322:             goto bad_reg;
        !           323:         }
        !           324:     } else if (offset < 0x180) {
        !           325:         /* Interrupt Set Enable.  */
        !           326:         irq = (offset - 0x100) * 8;
        !           327:         if (irq >= GIC_NIRQ)
        !           328:             goto bad_reg;
        !           329:         for (i = 0; i < 8; i++) {
        !           330:             if (value & (1 << i)) {
        !           331:                 if (!GIC_TEST_ENABLED(irq + i))
        !           332:                     DPRINTF("Enabled IRQ %d\n", irq + i);
        !           333:                 GIC_SET_ENABLED(irq + i);
        !           334:                 /* If a raised level triggered IRQ enabled then mark
        !           335:                    is as pending.  */
        !           336:                 if (GIC_TEST_LEVEL(irq + i) && !GIC_TEST_TRIGGER(irq + i))
        !           337:                     GIC_SET_PENDING(irq + i);
        !           338:             }
        !           339:         }
        !           340:     } else if (offset < 0x200) {
        !           341:         /* Interrupt Clear Enable.  */
        !           342:         irq = (offset - 0x180) * 8;
        !           343:         if (irq >= GIC_NIRQ)
        !           344:             goto bad_reg;
        !           345:         for (i = 0; i < 8; i++) {
        !           346:             if (value & (1 << i)) {
        !           347:                 if (GIC_TEST_ENABLED(irq + i))
        !           348:                     DPRINTF("Disabled IRQ %d\n", irq + i);
        !           349:                 GIC_CLEAR_ENABLED(irq + i);
        !           350:             }
        !           351:         }
        !           352:     } else if (offset < 0x280) {
        !           353:         /* Interrupt Set Pending.  */
        !           354:         irq = (offset - 0x200) * 8;
        !           355:         if (irq >= GIC_NIRQ)
        !           356:             goto bad_reg;
        !           357:         for (i = 0; i < 8; i++) {
        !           358:             if (value & (1 << i)) {
        !           359:                 GIC_SET_PENDING(irq + i);
        !           360:             }
        !           361:         }
        !           362:     } else if (offset < 0x300) {
        !           363:         /* Interrupt Clear Pending.  */
        !           364:         irq = (offset - 0x280) * 8;
        !           365:         if (irq >= GIC_NIRQ)
        !           366:             goto bad_reg;
        !           367:         for (i = 0; i < 8; i++) {
        !           368:             if (value & (1 << i)) {
        !           369:                 GIC_CLEAR_PENDING(irq + i);
        !           370:             }
        !           371:         }
        !           372:     } else if (offset < 0x400) {
        !           373:         /* Interrupt Active.  */
        !           374:         goto bad_reg;
        !           375:     } else if (offset < 0x800) {
        !           376:         /* Interrupt Priority.  */
        !           377:         irq = offset - 0x400;
        !           378:         if (irq >= GIC_NIRQ)
        !           379:             goto bad_reg;
        !           380:         s->priority[irq] = value;
        !           381:     } else if (offset < 0xc00) {
        !           382:         /* Interrupt CPU Target.  */
        !           383:         irq = offset - 0x800;
        !           384:         if (irq >= GIC_NIRQ)
        !           385:             goto bad_reg;
        !           386:         s->irq_target[irq] = value;
        !           387:     } else if (offset < 0xf00) {
        !           388:         /* Interrupt Configuration.  */
        !           389:         irq = (offset - 0xc00) * 4;
        !           390:         if (irq >= GIC_NIRQ)
        !           391:             goto bad_reg;
        !           392:         for (i = 0; i < 4; i++) {
        !           393:             if (value & (1 << (i * 2))) {
        !           394:                 GIC_SET_MODEL(irq + i);
        !           395:             } else {
        !           396:                 GIC_CLEAR_MODEL(irq + i);
        !           397:             }
        !           398:             if (value & (2 << (i * 2))) {
        !           399:                 GIC_SET_TRIGGER(irq + i);
        !           400:             } else {
        !           401:                 GIC_CLEAR_TRIGGER(irq + i);
        !           402:             }
        !           403:         }
        !           404:     } else {
        !           405:         /* 0xf00 is only handled for word writes.  */
        !           406:         goto bad_reg;
        !           407:     }
        !           408:     gic_update(s);
        !           409:     return;
        !           410: bad_reg:
        !           411:     cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", offset);
        !           412: }
        !           413: 
        !           414: static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
        !           415:                             uint32_t value)
        !           416: {
        !           417:     gic_state *s = (gic_state *)opaque;
        !           418:     if (offset - s->base == 0xf00) {
        !           419:         GIC_SET_PENDING(value & 0x3ff);
        !           420:         gic_update(s);
        !           421:         return;
        !           422:     }
        !           423:     gic_dist_writeb(opaque, offset, value & 0xff);
        !           424:     gic_dist_writeb(opaque, offset + 1, value >> 8);
        !           425: }
        !           426: 
        !           427: static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
        !           428:                             uint32_t value)
        !           429: {
        !           430:     gic_dist_writew(opaque, offset, value & 0xffff);
        !           431:     gic_dist_writew(opaque, offset + 2, value >> 16);
        !           432: }
        !           433: 
        !           434: static CPUReadMemoryFunc *gic_dist_readfn[] = {
        !           435:    gic_dist_readb,
        !           436:    gic_dist_readw,
        !           437:    gic_dist_readl
        !           438: };
        !           439: 
        !           440: static CPUWriteMemoryFunc *gic_dist_writefn[] = {
        !           441:    gic_dist_writeb,
        !           442:    gic_dist_writew,
        !           443:    gic_dist_writel
        !           444: };
        !           445: 
        !           446: static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset)
        !           447: {
        !           448:     gic_state *s = (gic_state *)opaque;
        !           449:     offset -= s->base;
        !           450:     switch (offset) {
        !           451:     case 0x00: /* Control */
        !           452:         return s->cpu_enabled;
        !           453:     case 0x04: /* Priority mask */
        !           454:         return s->priority_mask;
        !           455:     case 0x08: /* Binary Point */
        !           456:         /* ??? Not implemented.  */
        !           457:         return 0;
        !           458:     case 0x0c: /* Acknowledge */
        !           459:         return gic_acknowledge_irq(s);
        !           460:     case 0x14: /* Runing Priority */
        !           461:         return s->running_priority;
        !           462:     case 0x18: /* Highest Pending Interrupt */
        !           463:         return s->current_pending;
        !           464:     default:
        !           465:         cpu_abort (cpu_single_env, "gic_cpu_writeb: Bad offset %x\n", offset);
        !           466:         return 0;
        !           467:     }
        !           468: }
        !           469: 
        !           470: static void gic_cpu_write(void *opaque, target_phys_addr_t offset,
        !           471:                           uint32_t value)
        !           472: {
        !           473:     gic_state *s = (gic_state *)opaque;
        !           474:     offset -= s->base;
        !           475:     switch (offset) {
        !           476:     case 0x00: /* Control */
        !           477:         s->cpu_enabled = (value & 1);
        !           478:         DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis");
        !           479:         break;
        !           480:     case 0x04: /* Priority mask */
        !           481:         s->priority_mask = (value & 0x3ff);
        !           482:         break;
        !           483:     case 0x08: /* Binary Point */
        !           484:         /* ??? Not implemented.  */
        !           485:         break;
        !           486:     case 0x10: /* End Of Interrupt */
        !           487:         return gic_complete_irq(s, value & 0x3ff);
        !           488:     default:
        !           489:         cpu_abort (cpu_single_env, "gic_cpu_writeb: Bad offset %x\n", offset);
        !           490:         return;
        !           491:     }
        !           492:     gic_update(s);
        !           493: }
        !           494: 
        !           495: static CPUReadMemoryFunc *gic_cpu_readfn[] = {
        !           496:    gic_cpu_read,
        !           497:    gic_cpu_read,
        !           498:    gic_cpu_read
        !           499: };
        !           500: 
        !           501: static CPUWriteMemoryFunc *gic_cpu_writefn[] = {
        !           502:    gic_cpu_write,
        !           503:    gic_cpu_write,
        !           504:    gic_cpu_write
        !           505: };
        !           506: 
        !           507: static void gic_reset(gic_state *s)
        !           508: {
        !           509:     int i;
        !           510:     memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
        !           511:     s->priority_mask = 0xf0;
        !           512:     s->current_pending = 1023;
        !           513:     s->running_irq = 1023;
        !           514:     s->running_priority = 0x100;
        !           515:     for (i = 0; i < 15; i++) {
        !           516:         GIC_SET_ENABLED(i);
        !           517:         GIC_SET_TRIGGER(i);
        !           518:     }
        !           519:     s->enabled = 0;
        !           520:     s->cpu_enabled = 0;
        !           521: }
        !           522: 
        !           523: void *arm_gic_init(uint32_t base, void *parent, int parent_irq)
        !           524: {
        !           525:     gic_state *s;
        !           526:     int iomemtype;
        !           527: 
        !           528:     s = (gic_state *)qemu_mallocz(sizeof(gic_state));
        !           529:     if (!s)
        !           530:         return NULL;
        !           531:     s->handler = gic_set_irq;
        !           532:     s->parent = parent;
        !           533:     s->parent_irq = parent_irq;
        !           534:     if (base != 0xffffffff) {
        !           535:         iomemtype = cpu_register_io_memory(0, gic_cpu_readfn,
        !           536:                                            gic_cpu_writefn, s);
        !           537:         cpu_register_physical_memory(base, 0x00000fff, iomemtype);
        !           538:         iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
        !           539:                                            gic_dist_writefn, s);
        !           540:         cpu_register_physical_memory(base + 0x1000, 0x00000fff, iomemtype);
        !           541:         s->base = base;
        !           542:     } else {
        !           543:         s->base = 0;
        !           544:     }
        !           545:     gic_reset(s);
        !           546:     return s;
        !           547: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.