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

1.1     ! root        1: /*
        !             2:  * QEMU DMA emulation
        !             3:  *
        !             4:  * Copyright (c) 2003-2004 Vassili Karpov (malc)
        !             5:  *
        !             6:  * Permission is hereby granted, free of charge, to any person obtaining a copy
        !             7:  * of this software and associated documentation files (the "Software"), to deal
        !             8:  * in the Software without restriction, including without limitation the rights
        !             9:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        !            10:  * copies of the Software, and to permit persons to whom the Software is
        !            11:  * furnished to do so, subject to the following conditions:
        !            12:  *
        !            13:  * The above copyright notice and this permission notice shall be included in
        !            14:  * all copies or substantial portions of the Software.
        !            15:  *
        !            16:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        !            17:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            18:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            19:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            20:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        !            21:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        !            22:  * THE SOFTWARE.
        !            23:  */
        !            24: #include "vl.h"
        !            25: 
        !            26: /* #define DEBUG_DMA */
        !            27: 
        !            28: #define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
        !            29: #ifdef DEBUG_DMA
        !            30: #define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__)
        !            31: #define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
        !            32: #define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__)
        !            33: #else
        !            34: #define lwarn(...)
        !            35: #define linfo(...)
        !            36: #define ldebug(...)
        !            37: #endif
        !            38: 
        !            39: #define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0])))
        !            40: 
        !            41: struct dma_regs {
        !            42:     int now[2];
        !            43:     uint16_t base[2];
        !            44:     uint8_t mode;
        !            45:     uint8_t page;
        !            46:     uint8_t pageh;
        !            47:     uint8_t dack;
        !            48:     uint8_t eop;
        !            49:     DMA_transfer_handler transfer_handler;
        !            50:     void *opaque;
        !            51: };
        !            52: 
        !            53: #define ADDR 0
        !            54: #define COUNT 1
        !            55: 
        !            56: static struct dma_cont {
        !            57:     uint8_t status;
        !            58:     uint8_t command;
        !            59:     uint8_t mask;
        !            60:     uint8_t flip_flop;
        !            61:     int dshift;
        !            62:     struct dma_regs regs[4];
        !            63: } dma_controllers[2];
        !            64: 
        !            65: enum {
        !            66:     CMD_MEMORY_TO_MEMORY = 0x01,
        !            67:     CMD_FIXED_ADDRESS    = 0x02,
        !            68:     CMD_BLOCK_CONTROLLER = 0x04,
        !            69:     CMD_COMPRESSED_TIME  = 0x08,
        !            70:     CMD_CYCLIC_PRIORITY  = 0x10,
        !            71:     CMD_EXTENDED_WRITE   = 0x20,
        !            72:     CMD_LOW_DREQ         = 0x40,
        !            73:     CMD_LOW_DACK         = 0x80,
        !            74:     CMD_NOT_SUPPORTED    = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS
        !            75:     | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE
        !            76:     | CMD_LOW_DREQ | CMD_LOW_DACK
        !            77: 
        !            78: };
        !            79: 
        !            80: static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
        !            81: 
        !            82: static void write_page (void *opaque, uint32_t nport, uint32_t data)
        !            83: {
        !            84:     struct dma_cont *d = opaque;
        !            85:     int ichan;
        !            86: 
        !            87:     ichan = channels[nport & 7];
        !            88:     if (-1 == ichan) {
        !            89:         dolog ("invalid channel %#x %#x\n", nport, data);
        !            90:         return;
        !            91:     }
        !            92:     d->regs[ichan].page = data;
        !            93: }
        !            94: 
        !            95: static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
        !            96: {
        !            97:     struct dma_cont *d = opaque;
        !            98:     int ichan;
        !            99: 
        !           100:     ichan = channels[nport & 7];
        !           101:     if (-1 == ichan) {
        !           102:         dolog ("invalid channel %#x %#x\n", nport, data);
        !           103:         return;
        !           104:     }
        !           105:     d->regs[ichan].pageh = data;
        !           106: }
        !           107: 
        !           108: static uint32_t read_page (void *opaque, uint32_t nport)
        !           109: {
        !           110:     struct dma_cont *d = opaque;
        !           111:     int ichan;
        !           112: 
        !           113:     ichan = channels[nport & 7];
        !           114:     if (-1 == ichan) {
        !           115:         dolog ("invalid channel read %#x\n", nport);
        !           116:         return 0;
        !           117:     }
        !           118:     return d->regs[ichan].page;
        !           119: }
        !           120: 
        !           121: static uint32_t read_pageh (void *opaque, uint32_t nport)
        !           122: {
        !           123:     struct dma_cont *d = opaque;
        !           124:     int ichan;
        !           125: 
        !           126:     ichan = channels[nport & 7];
        !           127:     if (-1 == ichan) {
        !           128:         dolog ("invalid channel read %#x\n", nport);
        !           129:         return 0;
        !           130:     }
        !           131:     return d->regs[ichan].pageh;
        !           132: }
        !           133: 
        !           134: static inline void init_chan (struct dma_cont *d, int ichan)
        !           135: {
        !           136:     struct dma_regs *r;
        !           137: 
        !           138:     r = d->regs + ichan;
        !           139:     r->now[ADDR] = r->base[ADDR] << d->dshift;
        !           140:     r->now[COUNT] = 0;
        !           141: }
        !           142: 
        !           143: static inline int getff (struct dma_cont *d)
        !           144: {
        !           145:     int ff;
        !           146: 
        !           147:     ff = d->flip_flop;
        !           148:     d->flip_flop = !ff;
        !           149:     return ff;
        !           150: }
        !           151: 
        !           152: static uint32_t read_chan (void *opaque, uint32_t nport)
        !           153: {
        !           154:     struct dma_cont *d = opaque;
        !           155:     int ichan, nreg, iport, ff, val, dir;
        !           156:     struct dma_regs *r;
        !           157: 
        !           158:     iport = (nport >> d->dshift) & 0x0f;
        !           159:     ichan = iport >> 1;
        !           160:     nreg = iport & 1;
        !           161:     r = d->regs + ichan;
        !           162: 
        !           163:     dir = ((r->mode >> 5) & 1) ? -1 : 1;
        !           164:     ff = getff (d);
        !           165:     if (nreg)
        !           166:         val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
        !           167:     else
        !           168:         val = r->now[ADDR] + r->now[COUNT] * dir;
        !           169: 
        !           170:     ldebug ("read_chan %#x -> %d\n", iport, val);
        !           171:     return (val >> (d->dshift + (ff << 3))) & 0xff;
        !           172: }
        !           173: 
        !           174: static void write_chan (void *opaque, uint32_t nport, uint32_t data)
        !           175: {
        !           176:     struct dma_cont *d = opaque;
        !           177:     int iport, ichan, nreg;
        !           178:     struct dma_regs *r;
        !           179: 
        !           180:     iport = (nport >> d->dshift) & 0x0f;
        !           181:     ichan = iport >> 1;
        !           182:     nreg = iport & 1;
        !           183:     r = d->regs + ichan;
        !           184:     if (getff (d)) {
        !           185:         r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
        !           186:         init_chan (d, ichan);
        !           187:     } else {
        !           188:         r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
        !           189:     }
        !           190: }
        !           191: 
        !           192: static void write_cont (void *opaque, uint32_t nport, uint32_t data)
        !           193: {
        !           194:     struct dma_cont *d = opaque;
        !           195:     int iport, ichan = 0;
        !           196: 
        !           197:     iport = (nport >> d->dshift) & 0x0f;
        !           198:     switch (iport) {
        !           199:     case 0x08:                  /* command */
        !           200:         if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
        !           201:             dolog ("command %#x not supported\n", data);
        !           202:             return;
        !           203:         }
        !           204:         d->command = data;
        !           205:         break;
        !           206: 
        !           207:     case 0x09:
        !           208:         ichan = data & 3;
        !           209:         if (data & 4) {
        !           210:             d->status |= 1 << (ichan + 4);
        !           211:         }
        !           212:         else {
        !           213:             d->status &= ~(1 << (ichan + 4));
        !           214:         }
        !           215:         d->status &= ~(1 << ichan);
        !           216:         break;
        !           217: 
        !           218:     case 0x0a:                  /* single mask */
        !           219:         if (data & 4)
        !           220:             d->mask |= 1 << (data & 3);
        !           221:         else
        !           222:             d->mask &= ~(1 << (data & 3));
        !           223:         break;
        !           224: 
        !           225:     case 0x0b:                  /* mode */
        !           226:         {
        !           227:             ichan = data & 3;
        !           228: #ifdef DEBUG_DMA
        !           229:             {
        !           230:                 int op, ai, dir, opmode;
        !           231:                 op = (data >> 2) & 3;
        !           232:                 ai = (data >> 4) & 1;
        !           233:                 dir = (data >> 5) & 1;
        !           234:                 opmode = (data >> 6) & 3;
        !           235: 
        !           236:                 linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
        !           237:                        ichan, op, ai, dir, opmode);
        !           238:             }
        !           239: #endif
        !           240:             d->regs[ichan].mode = data;
        !           241:             break;
        !           242:         }
        !           243: 
        !           244:     case 0x0c:                  /* clear flip flop */
        !           245:         d->flip_flop = 0;
        !           246:         break;
        !           247: 
        !           248:     case 0x0d:                  /* reset */
        !           249:         d->flip_flop = 0;
        !           250:         d->mask = ~0;
        !           251:         d->status = 0;
        !           252:         d->command = 0;
        !           253:         break;
        !           254: 
        !           255:     case 0x0e:                  /* clear mask for all channels */
        !           256:         d->mask = 0;
        !           257:         break;
        !           258: 
        !           259:     case 0x0f:                  /* write mask for all channels */
        !           260:         d->mask = data;
        !           261:         break;
        !           262: 
        !           263:     default:
        !           264:         dolog ("unknown iport %#x\n", iport);
        !           265:         break;
        !           266:     }
        !           267: 
        !           268: #ifdef DEBUG_DMA
        !           269:     if (0xc != iport) {
        !           270:         linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
        !           271:                nport, ichan, data);
        !           272:     }
        !           273: #endif
        !           274: }
        !           275: 
        !           276: static uint32_t read_cont (void *opaque, uint32_t nport)
        !           277: {
        !           278:     struct dma_cont *d = opaque;
        !           279:     int iport, val;
        !           280: 
        !           281:     iport = (nport >> d->dshift) & 0x0f;
        !           282:     switch (iport) {
        !           283:     case 0x08:                  /* status */
        !           284:         val = d->status;
        !           285:         d->status &= 0xf0;
        !           286:         break;
        !           287:     case 0x0f:                  /* mask */
        !           288:         val = d->mask;
        !           289:         break;
        !           290:     default:
        !           291:         val = 0;
        !           292:         break;
        !           293:     }
        !           294: 
        !           295:     ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
        !           296:     return val;
        !           297: }
        !           298: 
        !           299: int DMA_get_channel_mode (int nchan)
        !           300: {
        !           301:     return dma_controllers[nchan > 3].regs[nchan & 3].mode;
        !           302: }
        !           303: 
        !           304: void DMA_hold_DREQ (int nchan)
        !           305: {
        !           306:     int ncont, ichan;
        !           307: 
        !           308:     ncont = nchan > 3;
        !           309:     ichan = nchan & 3;
        !           310:     linfo ("held cont=%d chan=%d\n", ncont, ichan);
        !           311:     dma_controllers[ncont].status |= 1 << (ichan + 4);
        !           312: }
        !           313: 
        !           314: void DMA_release_DREQ (int nchan)
        !           315: {
        !           316:     int ncont, ichan;
        !           317: 
        !           318:     ncont = nchan > 3;
        !           319:     ichan = nchan & 3;
        !           320:     linfo ("released cont=%d chan=%d\n", ncont, ichan);
        !           321:     dma_controllers[ncont].status &= ~(1 << (ichan + 4));
        !           322: }
        !           323: 
        !           324: static void channel_run (int ncont, int ichan)
        !           325: {
        !           326:     int n;
        !           327:     struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
        !           328: #ifdef DEBUG_DMA
        !           329:     int dir, opmode;
        !           330: 
        !           331:     dir = (r->mode >> 5) & 1;
        !           332:     opmode = (r->mode >> 6) & 3;
        !           333: 
        !           334:     if (dir) {
        !           335:         dolog ("DMA in address decrement mode\n");
        !           336:     }
        !           337:     if (opmode != 1) {
        !           338:         dolog ("DMA not in single mode select %#x\n", opmode);
        !           339:     }
        !           340: #endif
        !           341: 
        !           342:     r = dma_controllers[ncont].regs + ichan;
        !           343:     n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
        !           344:                              r->now[COUNT], (r->base[COUNT] + 1) << ncont);
        !           345:     r->now[COUNT] = n;
        !           346:     ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
        !           347: }
        !           348: 
        !           349: void DMA_run (void)
        !           350: {
        !           351:     struct dma_cont *d;
        !           352:     int icont, ichan;
        !           353: 
        !           354:     d = dma_controllers;
        !           355: 
        !           356:     for (icont = 0; icont < 2; icont++, d++) {
        !           357:         for (ichan = 0; ichan < 4; ichan++) {
        !           358:             int mask;
        !           359: 
        !           360:             mask = 1 << ichan;
        !           361: 
        !           362:             if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4))))
        !           363:                 channel_run (icont, ichan);
        !           364:         }
        !           365:     }
        !           366: }
        !           367: 
        !           368: void DMA_register_channel (int nchan,
        !           369:                            DMA_transfer_handler transfer_handler,
        !           370:                            void *opaque)
        !           371: {
        !           372:     struct dma_regs *r;
        !           373:     int ichan, ncont;
        !           374: 
        !           375:     ncont = nchan > 3;
        !           376:     ichan = nchan & 3;
        !           377: 
        !           378:     r = dma_controllers[ncont].regs + ichan;
        !           379:     r->transfer_handler = transfer_handler;
        !           380:     r->opaque = opaque;
        !           381: }
        !           382: 
        !           383: int DMA_read_memory (int nchan, void *buf, int pos, int len)
        !           384: {
        !           385:     struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
        !           386:     target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
        !           387: 
        !           388:     if (r->mode & 0x20) {
        !           389:         int i;
        !           390:         uint8_t *p = buf;
        !           391: 
        !           392:         cpu_physical_memory_read (addr - pos - len, buf, len);
        !           393:         /* What about 16bit transfers? */
        !           394:         for (i = 0; i < len >> 1; i++) {
        !           395:             uint8_t b = p[len - i - 1];
        !           396:             p[i] = b;
        !           397:         }
        !           398:     }
        !           399:     else
        !           400:         cpu_physical_memory_read (addr + pos, buf, len);
        !           401: 
        !           402:     return len;
        !           403: }
        !           404: 
        !           405: int DMA_write_memory (int nchan, void *buf, int pos, int len)
        !           406: {
        !           407:     struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
        !           408:     target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
        !           409: 
        !           410:     if (r->mode & 0x20) {
        !           411:         int i;
        !           412:         uint8_t *p = buf;
        !           413: 
        !           414:         cpu_physical_memory_write (addr - pos - len, buf, len);
        !           415:         /* What about 16bit transfers? */
        !           416:         for (i = 0; i < len; i++) {
        !           417:             uint8_t b = p[len - i - 1];
        !           418:             p[i] = b;
        !           419:         }
        !           420:     }
        !           421:     else
        !           422:         cpu_physical_memory_write (addr + pos, buf, len);
        !           423: 
        !           424:     return len;
        !           425: }
        !           426: 
        !           427: /* request the emulator to transfer a new DMA memory block ASAP */
        !           428: void DMA_schedule(int nchan)
        !           429: {
        !           430:     cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
        !           431: }
        !           432: 
        !           433: static void dma_reset(void *opaque)
        !           434: {
        !           435:     struct dma_cont *d = opaque;
        !           436:     write_cont (d, (0x0d << d->dshift), 0);
        !           437: }
        !           438: 
        !           439: /* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
        !           440: static void dma_init2(struct dma_cont *d, int base, int dshift,
        !           441:                       int page_base, int pageh_base)
        !           442: {
        !           443:     const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 };
        !           444:     int i;
        !           445: 
        !           446:     d->dshift = dshift;
        !           447:     for (i = 0; i < 8; i++) {
        !           448:         register_ioport_write (base + (i << dshift), 1, 1, write_chan, d);
        !           449:         register_ioport_read (base + (i << dshift), 1, 1, read_chan, d);
        !           450:     }
        !           451:     for (i = 0; i < LENOFA (page_port_list); i++) {
        !           452:         register_ioport_write (page_base + page_port_list[i], 1, 1,
        !           453:                                write_page, d);
        !           454:         register_ioport_read (page_base + page_port_list[i], 1, 1,
        !           455:                               read_page, d);
        !           456:         if (pageh_base >= 0) {
        !           457:             register_ioport_write (pageh_base + page_port_list[i], 1, 1,
        !           458:                                    write_pageh, d);
        !           459:             register_ioport_read (pageh_base + page_port_list[i], 1, 1,
        !           460:                                   read_pageh, d);
        !           461:         }
        !           462:     }
        !           463:     for (i = 0; i < 8; i++) {
        !           464:         register_ioport_write (base + ((i + 8) << dshift), 1, 1,
        !           465:                                write_cont, d);
        !           466:         register_ioport_read (base + ((i + 8) << dshift), 1, 1,
        !           467:                               read_cont, d);
        !           468:     }
        !           469:     qemu_register_reset(dma_reset, d);
        !           470:     dma_reset(d);
        !           471: }
        !           472: 
        !           473: static void dma_save (QEMUFile *f, void *opaque)
        !           474: {
        !           475:     struct dma_cont *d = opaque;
        !           476:     int i;
        !           477: 
        !           478:     /* qemu_put_8s (f, &d->status); */
        !           479:     qemu_put_8s (f, &d->command);
        !           480:     qemu_put_8s (f, &d->mask);
        !           481:     qemu_put_8s (f, &d->flip_flop);
        !           482:     qemu_put_be32s (f, &d->dshift);
        !           483: 
        !           484:     for (i = 0; i < 4; ++i) {
        !           485:         struct dma_regs *r = &d->regs[i];
        !           486:         qemu_put_be32s (f, &r->now[0]);
        !           487:         qemu_put_be32s (f, &r->now[1]);
        !           488:         qemu_put_be16s (f, &r->base[0]);
        !           489:         qemu_put_be16s (f, &r->base[1]);
        !           490:         qemu_put_8s (f, &r->mode);
        !           491:         qemu_put_8s (f, &r->page);
        !           492:         qemu_put_8s (f, &r->pageh);
        !           493:         qemu_put_8s (f, &r->dack);
        !           494:         qemu_put_8s (f, &r->eop);
        !           495:     }
        !           496: }
        !           497: 
        !           498: static int dma_load (QEMUFile *f, void *opaque, int version_id)
        !           499: {
        !           500:     struct dma_cont *d = opaque;
        !           501:     int i;
        !           502: 
        !           503:     if (version_id != 1)
        !           504:         return -EINVAL;
        !           505: 
        !           506:     /* qemu_get_8s (f, &d->status); */
        !           507:     qemu_get_8s (f, &d->command);
        !           508:     qemu_get_8s (f, &d->mask);
        !           509:     qemu_get_8s (f, &d->flip_flop);
        !           510:     qemu_get_be32s (f, &d->dshift);
        !           511: 
        !           512:     for (i = 0; i < 4; ++i) {
        !           513:         struct dma_regs *r = &d->regs[i];
        !           514:         qemu_get_be32s (f, &r->now[0]);
        !           515:         qemu_get_be32s (f, &r->now[1]);
        !           516:         qemu_get_be16s (f, &r->base[0]);
        !           517:         qemu_get_be16s (f, &r->base[1]);
        !           518:         qemu_get_8s (f, &r->mode);
        !           519:         qemu_get_8s (f, &r->page);
        !           520:         qemu_get_8s (f, &r->pageh);
        !           521:         qemu_get_8s (f, &r->dack);
        !           522:         qemu_get_8s (f, &r->eop);
        !           523:     }
        !           524:     return 0;
        !           525: }
        !           526: 
        !           527: void DMA_init (int high_page_enable)
        !           528: {
        !           529:     dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
        !           530:               high_page_enable ? 0x480 : -1);
        !           531:     dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
        !           532:               high_page_enable ? 0x488 : -1);
        !           533:     register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]);
        !           534:     register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]);
        !           535: }

unix.superglobalmegacorp.com

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