When running device-introspect-test, a memory leak occurred in the
mss_timer_init function, so use ptimer_free() in the finalize function to avoid
it.
ASAN shows memory leak stack:
Indirect leak of 192 byte(s) in 2 object(s) allocated from:
    #0 0xffffab97e1f0 in __interceptor_calloc (/lib64/libasan.so.5+0xee1f0)
    #1 0xffffab256800 in g_malloc0 (/lib64/libglib-2.0.so.0+0x56800)
    #2 0xaaabf555db78 in ptimer_init /qemu/hw/core/ptimer.c:432
    #3 0xaaabf58a0010 in mss_timer_init /qemu/hw/timer/mss-timer.c:235
    #4 0xaaabf6339f6c in object_initialize_with_type /qemu/qom/object.c:515
    #5 0xaaabf633ca04 in object_initialize_child_with_propsv /qemu/qom/object.c:564
    #6 0xaaabf633cc08 in object_initialize_child_with_props /qemu/qom/object.c:547
    #7 0xaaabf5b8316c in m2sxxx_soc_initfn /qemu/hw/arm/msf2-soc.c:70
    #8 0xaaabf6339f6c in object_initialize_with_type /qemu/qom/object.c:515
    #9 0xaaabf633a1e0 in object_new_with_type /qemu/qom/object.c:729
    #10 0xaaabf6375e40 in qmp_device_list_properties /qemu/qom/qom-qmp-cmds.c:153
    #11 0xaaabf653d8ec in qmp_marshal_device_list_properties /qemu/qapi/qapi-commands-qdev.c:59
    #12 0xaaabf6587d08 in do_qmp_dispatch_bh /qemu/qapi/qmp-dispatch.c:110
Reported-by: Euler Robot <euler.robot@huawei.com>
Signed-off-by: Gan Qixin <ganqixin@huawei.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
		
	
		
			
				
	
	
		
			312 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			312 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Block model of System timer present in
 | |
|  * Microsemi's SmartFusion2 and SmartFusion SoCs.
 | |
|  *
 | |
|  * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>.
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
|  * of this software and associated documentation files (the "Software"), to deal
 | |
|  * in the Software without restriction, including without limitation the rights
 | |
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
|  * copies of the Software, and to permit persons to whom the Software is
 | |
|  * furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | |
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
|  * THE SOFTWARE.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "qemu/module.h"
 | |
| #include "qemu/log.h"
 | |
| #include "hw/irq.h"
 | |
| #include "hw/qdev-properties.h"
 | |
| #include "hw/timer/mss-timer.h"
 | |
| #include "migration/vmstate.h"
 | |
| 
 | |
| #ifndef MSS_TIMER_ERR_DEBUG
 | |
| #define MSS_TIMER_ERR_DEBUG  0
 | |
| #endif
 | |
| 
 | |
| #define DB_PRINT_L(lvl, fmt, args...) do { \
 | |
|     if (MSS_TIMER_ERR_DEBUG >= lvl) { \
 | |
|         qemu_log("%s: " fmt "\n", __func__, ## args); \
 | |
|     } \
 | |
| } while (0)
 | |
| 
 | |
| #define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
 | |
| 
 | |
| #define R_TIM_VAL         0
 | |
| #define R_TIM_LOADVAL     1
 | |
| #define R_TIM_BGLOADVAL   2
 | |
| #define R_TIM_CTRL        3
 | |
| #define R_TIM_RIS         4
 | |
| #define R_TIM_MIS         5
 | |
| 
 | |
| #define TIMER_CTRL_ENBL     (1 << 0)
 | |
| #define TIMER_CTRL_ONESHOT  (1 << 1)
 | |
| #define TIMER_CTRL_INTR     (1 << 2)
 | |
| #define TIMER_RIS_ACK       (1 << 0)
 | |
| #define TIMER_RST_CLR       (1 << 6)
 | |
| #define TIMER_MODE          (1 << 0)
 | |
| 
 | |
| static void timer_update_irq(struct Msf2Timer *st)
 | |
| {
 | |
|     bool isr, ier;
 | |
| 
 | |
|     isr = !!(st->regs[R_TIM_RIS] & TIMER_RIS_ACK);
 | |
|     ier = !!(st->regs[R_TIM_CTRL] & TIMER_CTRL_INTR);
 | |
|     qemu_set_irq(st->irq, (ier && isr));
 | |
| }
 | |
| 
 | |
| /* Must be called from within a ptimer_transaction_begin/commit block */
 | |
| static void timer_update(struct Msf2Timer *st)
 | |
| {
 | |
|     uint64_t count;
 | |
| 
 | |
|     if (!(st->regs[R_TIM_CTRL] & TIMER_CTRL_ENBL)) {
 | |
|         ptimer_stop(st->ptimer);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     count = st->regs[R_TIM_LOADVAL];
 | |
|     ptimer_set_limit(st->ptimer, count, 1);
 | |
|     ptimer_run(st->ptimer, 1);
 | |
| }
 | |
| 
 | |
| static uint64_t
 | |
| timer_read(void *opaque, hwaddr offset, unsigned int size)
 | |
| {
 | |
|     MSSTimerState *t = opaque;
 | |
|     hwaddr addr;
 | |
|     struct Msf2Timer *st;
 | |
|     uint32_t ret = 0;
 | |
|     int timer = 0;
 | |
|     int isr;
 | |
|     int ier;
 | |
| 
 | |
|     addr = offset >> 2;
 | |
|     /*
 | |
|      * Two independent timers has same base address.
 | |
|      * Based on address passed figure out which timer is being used.
 | |
|      */
 | |
|     if ((addr >= R_TIM1_MAX) && (addr < NUM_TIMERS * R_TIM1_MAX)) {
 | |
|         timer = 1;
 | |
|         addr -= R_TIM1_MAX;
 | |
|     }
 | |
| 
 | |
|     st = &t->timers[timer];
 | |
| 
 | |
|     switch (addr) {
 | |
|     case R_TIM_VAL:
 | |
|         ret = ptimer_get_count(st->ptimer);
 | |
|         break;
 | |
| 
 | |
|     case R_TIM_MIS:
 | |
|         isr = !!(st->regs[R_TIM_RIS] & TIMER_RIS_ACK);
 | |
|         ier = !!(st->regs[R_TIM_CTRL] & TIMER_CTRL_INTR);
 | |
|         ret = ier & isr;
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         if (addr < R_TIM1_MAX) {
 | |
|             ret = st->regs[addr];
 | |
|         } else {
 | |
|             qemu_log_mask(LOG_GUEST_ERROR,
 | |
|                         TYPE_MSS_TIMER": 64-bit mode not supported\n");
 | |
|             return ret;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     DB_PRINT("timer=%d 0x%" HWADDR_PRIx "=0x%" PRIx32, timer, offset,
 | |
|             ret);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static void
 | |
| timer_write(void *opaque, hwaddr offset,
 | |
|             uint64_t val64, unsigned int size)
 | |
| {
 | |
|     MSSTimerState *t = opaque;
 | |
|     hwaddr addr;
 | |
|     struct Msf2Timer *st;
 | |
|     int timer = 0;
 | |
|     uint32_t value = val64;
 | |
| 
 | |
|     addr = offset >> 2;
 | |
|     /*
 | |
|      * Two independent timers has same base address.
 | |
|      * Based on addr passed figure out which timer is being used.
 | |
|      */
 | |
|     if ((addr >= R_TIM1_MAX) && (addr < NUM_TIMERS * R_TIM1_MAX)) {
 | |
|         timer = 1;
 | |
|         addr -= R_TIM1_MAX;
 | |
|     }
 | |
| 
 | |
|     st = &t->timers[timer];
 | |
| 
 | |
|     DB_PRINT("addr=0x%" HWADDR_PRIx " val=0x%" PRIx32 " (timer=%d)", offset,
 | |
|             value, timer);
 | |
| 
 | |
|     switch (addr) {
 | |
|     case R_TIM_CTRL:
 | |
|         st->regs[R_TIM_CTRL] = value;
 | |
|         ptimer_transaction_begin(st->ptimer);
 | |
|         timer_update(st);
 | |
|         ptimer_transaction_commit(st->ptimer);
 | |
|         break;
 | |
| 
 | |
|     case R_TIM_RIS:
 | |
|         if (value & TIMER_RIS_ACK) {
 | |
|             st->regs[R_TIM_RIS] &= ~TIMER_RIS_ACK;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case R_TIM_LOADVAL:
 | |
|         st->regs[R_TIM_LOADVAL] = value;
 | |
|         if (st->regs[R_TIM_CTRL] & TIMER_CTRL_ENBL) {
 | |
|             ptimer_transaction_begin(st->ptimer);
 | |
|             timer_update(st);
 | |
|             ptimer_transaction_commit(st->ptimer);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case R_TIM_BGLOADVAL:
 | |
|         st->regs[R_TIM_BGLOADVAL] = value;
 | |
|         st->regs[R_TIM_LOADVAL] = value;
 | |
|         break;
 | |
| 
 | |
|     case R_TIM_VAL:
 | |
|     case R_TIM_MIS:
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         if (addr < R_TIM1_MAX) {
 | |
|             st->regs[addr] = value;
 | |
|         } else {
 | |
|             qemu_log_mask(LOG_GUEST_ERROR,
 | |
|                         TYPE_MSS_TIMER": 64-bit mode not supported\n");
 | |
|             return;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|     timer_update_irq(st);
 | |
| }
 | |
| 
 | |
| static const MemoryRegionOps timer_ops = {
 | |
|     .read = timer_read,
 | |
|     .write = timer_write,
 | |
|     .endianness = DEVICE_NATIVE_ENDIAN,
 | |
|     .valid = {
 | |
|         .min_access_size = 1,
 | |
|         .max_access_size = 4
 | |
|     }
 | |
| };
 | |
| 
 | |
| static void timer_hit(void *opaque)
 | |
| {
 | |
|     struct Msf2Timer *st = opaque;
 | |
| 
 | |
|     st->regs[R_TIM_RIS] |= TIMER_RIS_ACK;
 | |
| 
 | |
|     if (!(st->regs[R_TIM_CTRL] & TIMER_CTRL_ONESHOT)) {
 | |
|         timer_update(st);
 | |
|     }
 | |
|     timer_update_irq(st);
 | |
| }
 | |
| 
 | |
| static void mss_timer_init(Object *obj)
 | |
| {
 | |
|     MSSTimerState *t = MSS_TIMER(obj);
 | |
|     int i;
 | |
| 
 | |
|     /* Init all the ptimers.  */
 | |
|     for (i = 0; i < NUM_TIMERS; i++) {
 | |
|         struct Msf2Timer *st = &t->timers[i];
 | |
| 
 | |
|         st->ptimer = ptimer_init(timer_hit, st, PTIMER_POLICY_DEFAULT);
 | |
|         ptimer_transaction_begin(st->ptimer);
 | |
|         ptimer_set_freq(st->ptimer, t->freq_hz);
 | |
|         ptimer_transaction_commit(st->ptimer);
 | |
|         sysbus_init_irq(SYS_BUS_DEVICE(obj), &st->irq);
 | |
|     }
 | |
| 
 | |
|     memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, TYPE_MSS_TIMER,
 | |
|                           NUM_TIMERS * R_TIM1_MAX * 4);
 | |
|     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &t->mmio);
 | |
| }
 | |
| 
 | |
| static void mss_timer_finalize(Object *obj)
 | |
| {
 | |
|     MSSTimerState *t = MSS_TIMER(obj);
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < NUM_TIMERS; i++) {
 | |
|         struct Msf2Timer *st = &t->timers[i];
 | |
| 
 | |
|         ptimer_free(st->ptimer);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static const VMStateDescription vmstate_timers = {
 | |
|     .name = "mss-timer-block",
 | |
|     .version_id = 1,
 | |
|     .minimum_version_id = 1,
 | |
|     .fields = (VMStateField[]) {
 | |
|         VMSTATE_PTIMER(ptimer, struct Msf2Timer),
 | |
|         VMSTATE_UINT32_ARRAY(regs, struct Msf2Timer, R_TIM1_MAX),
 | |
|         VMSTATE_END_OF_LIST()
 | |
|     }
 | |
| };
 | |
| 
 | |
| static const VMStateDescription vmstate_mss_timer = {
 | |
|     .name = TYPE_MSS_TIMER,
 | |
|     .version_id = 1,
 | |
|     .minimum_version_id = 1,
 | |
|     .fields = (VMStateField[]) {
 | |
|         VMSTATE_UINT32(freq_hz, MSSTimerState),
 | |
|         VMSTATE_STRUCT_ARRAY(timers, MSSTimerState, NUM_TIMERS, 0,
 | |
|                 vmstate_timers, struct Msf2Timer),
 | |
|         VMSTATE_END_OF_LIST()
 | |
|     }
 | |
| };
 | |
| 
 | |
| static Property mss_timer_properties[] = {
 | |
|     /* Libero GUI shows 100Mhz as default for clocks */
 | |
|     DEFINE_PROP_UINT32("clock-frequency", MSSTimerState, freq_hz,
 | |
|                       100 * 1000000),
 | |
|     DEFINE_PROP_END_OF_LIST(),
 | |
| };
 | |
| 
 | |
| static void mss_timer_class_init(ObjectClass *klass, void *data)
 | |
| {
 | |
|     DeviceClass *dc = DEVICE_CLASS(klass);
 | |
| 
 | |
|     device_class_set_props(dc, mss_timer_properties);
 | |
|     dc->vmsd = &vmstate_mss_timer;
 | |
| }
 | |
| 
 | |
| static const TypeInfo mss_timer_info = {
 | |
|     .name          = TYPE_MSS_TIMER,
 | |
|     .parent        = TYPE_SYS_BUS_DEVICE,
 | |
|     .instance_size = sizeof(MSSTimerState),
 | |
|     .instance_init = mss_timer_init,
 | |
|     .instance_finalize = mss_timer_finalize,
 | |
|     .class_init    = mss_timer_class_init,
 | |
| };
 | |
| 
 | |
| static void mss_timer_register_types(void)
 | |
| {
 | |
|     type_register_static(&mss_timer_info);
 | |
| }
 | |
| 
 | |
| type_init(mss_timer_register_types)
 |