на PowerPC P2040/E500mc инструкция LD вызывает панику ядра во время извлечения карты PCI

Все, что я прочитал до сих пор, указывает на тот факт, что доступ к адресному пространству PCI во время извлечения карты вызовет панику ядра, если это не будет обработано в ядре machine_check_handler. Machine_check_handler для e500mc ищет EA (эффективный адрес) инструкции в регистре MCSRR0 и сравнивает его с адресным пространством PCI. Однако, поскольку этот адрес (EA) не находился в адресном пространстве PCI, в конечном итоге это вызвало панику ядра, поскольку его нельзя было обработать в обработчике прерывания проверки машины, поскольку адрес был неправильным адресом, который был сохранен ЦП в MCSRR0.

Хотя все GPR указывают на адреса BAR адресного пространства PCI из предыдущих инструкций процессора, но эффективный адрес, хранящийся в регистре MCSRR0, является тем же недопустимым физическим адресом, на который указывает NIP... MCSRR1 указывает на состояние машины (MSR) в момент прерывания и показывает установленные биты LD|GLD вместе с битом MCSRR1[RI]. так что это восстанавливаемое синхронное прерывание.

А поскольку доступ к адресу ЦП был на внешнем устройстве с горячим подключением, нам не нужно было вызывать сбой системы, даже если устройство отсутствует, и, следовательно, проверка ядра и безопасный возврат из прерывания.

У меня есть несколько вопросов по этой теме:

  1. Какие GPR используются для определения эффективного адреса инструкции LD. Бит LD установлен в регистре MCSR? Как узнать, какой режим адресации использовался для генерации эффективного адреса для инструкции LD?
  2. инструкция LD использует операнды rD, rA, rB, как узнать, какой режим расчета советника используется процессором. Судя по всему их 4. Кроме того, на какие GPR указывает каждый из этих операндов? Я не мог понять это по E500MCRM или powepc EREF.
  3. Поскольку мы записываем в адресное пространство PCI из пользовательского пространства, регистры устройства PCI сопоставляются с некоторым виртуальным адресным пространством в памяти процесса, в который мы записываем. Насколько я знаю, это не кэшированное отображение. Преобразование адреса ЦП в физический адрес устройства PCI для доступа к устройству PCI приводит к неправильному адресу, поскольку устройство PCI больше не подключено. Мое предположение заключалось в том, что, поскольку устройство больше не присутствует, возвращенный эффективный адрес был каким-то ненужным значением, которое вызвало эту панику ядра. Я не уверен, что так работает процессор.

Приветствуются любые предложения, помогающие моему пониманию. это глубоко внутри и за пределами моей компетенции. Я прошел через E500MCRM, P2040RM и powerpc EREF, но я не могу понять, почему я получаю неверный адрес вместо физического адреса PCI в эффективном адресе.


ядро - аварийный дамп


fujitsu:~$ fsl_pci_mcheck_exception-> SPRN_MCAR: 0x0
fsl_pci_mcheck_exception-> SPRN_MCSRR0: 0x0f6fec68
fsl_pci_mcheck_exception-> SPRN_MCSRR1: 0x2d002
fsl_pci_mcheck_exception-> SPRN_MCAR: 0x0
fsl_pci_mcheck_exception-> SPRN_DEAR: 0x0
fsl_pci_mcheck_exception-> current->pid: [8333]
fsl_pci_mcheck_exception-> after __get_user_inatomic(inst, &regs->nip):     0x0f6fec68(inst), 0x0f6fec68(regs->nip), 0x0(ret)

Machine check in kernel mode.
Caused by (from MCSR=a000): Load Error Report
Guarded Load Error Report
Oops: Machine check, sig: 7 [#1]
PREEMPT SMP NR_CPUS=4 P2041 RDB
Modules linked in: i2cBridge(O) interruptDriver_pb(O) cma_alloc(O) hwtp_drv(O) interruptDriver_wdt(O)
NIP: 0f6fec68 LR: 0f6fec4c CTR: 0f6faad4
REGS: e4ec5f10 TRAP: 0204   Tainted: G           O  (3.8.13-rt9+)
MSR: 0002d002 <CE,EE,PR,ME>  CR: 40044442  XER: 20000000
TASK = e57dc020[8333] 'RxManager' THREAD: e4ec4000 CPU: 3
GPR00: 0f6fec4c 52afea90 52b06910 50400000 52afeb50 00000003 a0105210 52afebfc 
GPR08: a1ffffff a0000000 0000000c a0000000 20044448 1032e800 52900000 00000006 
GPR16: 0f74f434 0f729d20 135a78a0 00200000 0fe28280 52aff4b0 00000000 0fe2a6c8 
GPR24: 52afec98 0f6cd268 135a7630 00105210 52afebfc 50400000 0f71d31c 00000003 
NIP [0f6fec68] 0xf6fec68
LR [0f6fec4c] 0xf6fec4c
Call Trace:
---[ end trace 2715d0da39427f69 ]---

вот код из fsl_pci.c, который вызывается из machine_check_handler


#ifdef CONFIG_E500
static int mcheck_handle_load(struct pt_regs *regs, u32 inst)
{
    unsigned int rd, ra, rb, d;

    rd = get_rt(inst);
    ra = get_ra(inst);
    rb = get_rb(inst);
    d = get_d(inst);

    printk(KERN_EMERG "%s==> rd==0x%x, ra=0x%x, rb=0x%x, d=0x%x\n", __FUNCTION__, rd, ra, rb, d);
    printk(KERN_EMERG "%s==> get_op(inst) = 0x%x\n", __FUNCTION__, get_op(inst));

    return 1;

    switch (get_op(inst)) {
    case 31:
        switch (get_xop(inst)) {
        case OP_31_XOP_LWZX:
        case OP_31_XOP_LWBRX:
            regs->gpr[rd] = 0xffffffff;
            break;

        case OP_31_XOP_LWZUX:
            regs->gpr[rd] = 0xffffffff;
            regs->gpr[ra] += regs->gpr[rb];
            break;

        case OP_31_XOP_LBZX:
            regs->gpr[rd] = 0xff;
            break;

        case OP_31_XOP_LBZUX:
            regs->gpr[rd] = 0xff;
            regs->gpr[ra] += regs->gpr[rb];
            break;

        case OP_31_XOP_LHZX:
        case OP_31_XOP_LHBRX:
            regs->gpr[rd] = 0xffff;
            break;

        case OP_31_XOP_LHZUX:
            regs->gpr[rd] = 0xffff;
            regs->gpr[ra] += regs->gpr[rb];
            break;

        default:
            return 0;
        }
        break;

    case OP_LWZ:
        regs->gpr[rd] = 0xffffffff;
        break;

    case OP_LWZU:
        regs->gpr[rd] = 0xffffffff;
        regs->gpr[ra] += (s16)d;
        break;

    case OP_LBZ:
        regs->gpr[rd] = 0xff;
        break;

    case OP_LBZU:
        regs->gpr[rd] = 0xff;
        regs->gpr[ra] += (s16)d;
        break;

    case OP_LHZ:
        regs->gpr[rd] = 0xffff;
        break;

    case OP_LHZU:
        regs->gpr[rd] = 0xffff;
        regs->gpr[ra] += (s16)d;
        break;

    default:
        return 0;
    }

    return 1;
}

static int is_in_pci_mem_space(phys_addr_t addr)
{
    struct pci_controller *hose;
    struct resource *res;
    int i;

    list_for_each_entry(hose, &hose_list, list_node) {
        if (!(hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG))
            continue;

        for (i = 0; i < 3; i++) {
            res = &hose->mem_resources[i];
            if ((res->flags & IORESOURCE_MEM) &&
                addr >= res->start && addr <= res->end)
                printk(KERN_EMERG "%s ==> returning from checking addresses\n", __FUNCTION__);
                return 1;
        }
    }

    printk(KERN_EMERG "%s ==> returning without checking addresses\n", __FUNCTION__);
    return 1;
}

int fsl_pci_mcheck_exception(struct pt_regs *regs)
{
    u32 inst;
    int ret;
    phys_addr_t addr = 0;

    /* Let KVM/QEMU deal with the exception */
    if (regs->msr & MSR_GS)
        return 0;

#ifdef CONFIG_PHYS_64BIT
    addr = mfspr(SPRN_MCARU);
    addr <<= 32;
#endif
    addr += mfspr(SPRN_MCSRR0);

    printk(KERN_EMERG "%s-> SPRN_MCAR: 0x%x\n", __FUNCTION__, addr);
    printk(KERN_EMERG "%s-> SPRN_MCSRR0: 0x%x\n", __FUNCTION__, mfspr(SPRN_MCSRR0));
    printk(KERN_EMERG "%s-> SPRN_MCSRR1: 0x%x\n", __FUNCTION__, mfspr(SPRN_MCSRR1));
    printk(KERN_EMERG "%s-> current->pid: 0x%x\n", __FUNCTION__, current->pid);

#ifdef CONFIG_E500
    if (mfspr(SPRN_EPCR) & SPRN_EPCR_ICM)
        addr = PFN_PHYS(vmalloc_to_pfn((void *)mfspr(SPRN_DEAR)));
    printk(KERN_EMERG "%s-> SPRN_DEAR: 0x%x\n", __FUNCTION__, addr);
#endif
    printk(KERN_EMERG "%s-> before get_user: 0x%x, 0x%x\n", __FUNCTION__, regs->nip, inst);
    if (is_in_pci_mem_space(addr)) {
        if (user_mode(regs)) {
            pagefault_disable();
           /* I am using __get_user_inatomic to get the instruction from the user
            space as any other get_user versions were resulting in -EFAULT as they can 
            sleep and this needs to be called from user context and we are in interrupt 
            context.
            */
            ret = __get_user_inatomic(inst, &regs->nip);
            pagefault_enable();
        } else {
            ret = probe_kernel_address(regs->nip, inst);
        }
        printk(KERN_EMERG "%s-> after get_user: 0x%x, 0x%x, 0x%d\n", __FUNCTION__, regs->nip, inst, ret);

        if (mcheck_handle_load(regs, inst)) {
            regs->nip += 4;
            printk(KERN_EMERG "%s-> after mcheck_handle load: 0x%x, 0x%x\n", __FUNCTION__, regs->nip, inst);
            return 1;
        }
    }

    return 0;
}
#endif

person Srinivas Pulukuru    schedule 15.05.2017    source источник
comment
Получаете ли вы дамп обратной трассировки/регистра при сбое? Если это так, пожалуйста, напишите об этом.   -  person mpe    schedule 15.05.2017
comment
Я добавил следующий код перед mcheck_handle_load, так как эта функция не смогла обработать (inst), возвращенный из get_user_inatomic или regs-›nip. Я решил добавить этот хак, чтобы увидеть, правильно ли возвращается прерывание и продолжается ли выполнение программы. E500MCRM четко объясняет, что ошибка LD является исправимой ошибкой. Я все еще хочу знать, как получить правильный адрес инструкции/физический адрес, вызвавший прерывание, и какие GPR используются для расчета EA.   -  person Srinivas Pulukuru    schedule 17.05.2017


Ответы (1)


Вот код, который я добавил, чтобы исправить панику ядра. Похоже, что regs->gpr[0] является адресом назначения инструкции LD, и увеличение указателя инструкции позаботилось о возврате из контекста прерывания. У меня все еще есть проблема проверки того, что это прерывание возникло из-за доступа к адресу устройства PCI. Прямо сейчас я закомментировал проверку диапазона адресов PCI, и без этой проверки я могу получить доступ к любому адресу без сбоя системы, что еще хуже.

да. Даже доступ по нулевому указателю больше не приводит к сбою системы. Я попробовал это с devmem2 и получил доступ к нулевому указателю, и вызов проходит через прерывание и благополучно возвращается после сброса журналов из обработчика прерываний.

regs->gpr[0] = 0xffffffff;
regs->nip += 4;
return 1;

if (mcheck_handle_load(regs, inst)) {
person Srinivas Pulukuru    schedule 17.05.2017