diff options
| -rw-r--r-- | drivers/edac/amd64_edac.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 4716fb561e6e..28f85c9e3afc 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c | |||
| @@ -434,4 +434,298 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, | |||
| 434 | } | 434 | } |
| 435 | EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info); | 435 | EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info); |
| 436 | 436 | ||
| 437 | /* | ||
| 438 | * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is | ||
| 439 | * assumed that sys_addr maps to the node given by mci. | ||
| 440 | * | ||
| 441 | * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section | ||
| 442 | * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a | ||
| 443 | * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled, | ||
| 444 | * then it is also involved in translating a SysAddr to a DramAddr. Sections | ||
| 445 | * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting. | ||
| 446 | * These parts of the documentation are unclear. I interpret them as follows: | ||
| 447 | * | ||
| 448 | * When node n receives a SysAddr, it processes the SysAddr as follows: | ||
| 449 | * | ||
| 450 | * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM | ||
| 451 | * Limit registers for node n. If the SysAddr is not within the range | ||
| 452 | * specified by the base and limit values, then node n ignores the Sysaddr | ||
| 453 | * (since it does not map to node n). Otherwise continue to step 2 below. | ||
| 454 | * | ||
| 455 | * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is | ||
| 456 | * disabled so skip to step 3 below. Otherwise see if the SysAddr is within | ||
| 457 | * the range of relocated addresses (starting at 0x100000000) from the DRAM | ||
| 458 | * hole. If not, skip to step 3 below. Else get the value of the | ||
| 459 | * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the | ||
| 460 | * offset defined by this value from the SysAddr. | ||
| 461 | * | ||
| 462 | * 3. Obtain the base address for node n from the DRAMBase field of the DRAM | ||
| 463 | * Base register for node n. To obtain the DramAddr, subtract the base | ||
| 464 | * address from the SysAddr, as shown near the start of section 3.4.4 (p.70). | ||
| 465 | */ | ||
| 466 | static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) | ||
| 467 | { | ||
| 468 | u64 dram_base, hole_base, hole_offset, hole_size, dram_addr; | ||
| 469 | int ret = 0; | ||
| 470 | |||
| 471 | dram_base = get_dram_base(mci); | ||
| 472 | |||
| 473 | ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, | ||
| 474 | &hole_size); | ||
| 475 | if (!ret) { | ||
| 476 | if ((sys_addr >= (1ull << 32)) && | ||
| 477 | (sys_addr < ((1ull << 32) + hole_size))) { | ||
| 478 | /* use DHAR to translate SysAddr to DramAddr */ | ||
| 479 | dram_addr = sys_addr - hole_offset; | ||
| 480 | |||
| 481 | debugf2("using DHAR to translate SysAddr 0x%lx to " | ||
| 482 | "DramAddr 0x%lx\n", | ||
| 483 | (unsigned long)sys_addr, | ||
| 484 | (unsigned long)dram_addr); | ||
| 485 | |||
| 486 | return dram_addr; | ||
| 487 | } | ||
| 488 | } | ||
| 489 | |||
| 490 | /* | ||
| 491 | * Translate the SysAddr to a DramAddr as shown near the start of | ||
| 492 | * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8 | ||
| 493 | * only deals with 40-bit values. Therefore we discard bits 63-40 of | ||
| 494 | * sys_addr below. If bit 39 of sys_addr is 1 then the bits we | ||
| 495 | * discard are all 1s. Otherwise the bits we discard are all 0s. See | ||
| 496 | * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture | ||
| 497 | * Programmer's Manual Volume 1 Application Programming. | ||
| 498 | */ | ||
| 499 | dram_addr = (sys_addr & 0xffffffffffull) - dram_base; | ||
| 500 | |||
| 501 | debugf2("using DRAM Base register to translate SysAddr 0x%lx to " | ||
| 502 | "DramAddr 0x%lx\n", (unsigned long)sys_addr, | ||
| 503 | (unsigned long)dram_addr); | ||
| 504 | return dram_addr; | ||
| 505 | } | ||
| 506 | |||
| 507 | /* | ||
| 508 | * @intlv_en is the value of the IntlvEn field from a DRAM Base register | ||
| 509 | * (section 3.4.4.1). Return the number of bits from a SysAddr that are used | ||
| 510 | * for node interleaving. | ||
| 511 | */ | ||
| 512 | static int num_node_interleave_bits(unsigned intlv_en) | ||
| 513 | { | ||
| 514 | static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 }; | ||
| 515 | int n; | ||
| 516 | |||
| 517 | BUG_ON(intlv_en > 7); | ||
| 518 | n = intlv_shift_table[intlv_en]; | ||
| 519 | return n; | ||
| 520 | } | ||
| 521 | |||
| 522 | /* Translate the DramAddr given by @dram_addr to an InputAddr. */ | ||
| 523 | static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr) | ||
| 524 | { | ||
| 525 | struct amd64_pvt *pvt; | ||
| 526 | int intlv_shift; | ||
| 527 | u64 input_addr; | ||
| 528 | |||
| 529 | pvt = mci->pvt_info; | ||
| 530 | |||
| 531 | /* | ||
| 532 | * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E) | ||
| 533 | * concerning translating a DramAddr to an InputAddr. | ||
| 534 | */ | ||
| 535 | intlv_shift = num_node_interleave_bits(pvt->dram_IntlvEn[0]); | ||
| 536 | input_addr = ((dram_addr >> intlv_shift) & 0xffffff000ull) + | ||
| 537 | (dram_addr & 0xfff); | ||
| 538 | |||
| 539 | debugf2(" Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n", | ||
| 540 | intlv_shift, (unsigned long)dram_addr, | ||
| 541 | (unsigned long)input_addr); | ||
| 542 | |||
| 543 | return input_addr; | ||
| 544 | } | ||
| 545 | |||
| 546 | /* | ||
| 547 | * Translate the SysAddr represented by @sys_addr to an InputAddr. It is | ||
| 548 | * assumed that @sys_addr maps to the node given by mci. | ||
| 549 | */ | ||
| 550 | static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr) | ||
| 551 | { | ||
| 552 | u64 input_addr; | ||
| 553 | |||
| 554 | input_addr = | ||
| 555 | dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr)); | ||
| 556 | |||
| 557 | debugf2("SysAdddr 0x%lx translates to InputAddr 0x%lx\n", | ||
| 558 | (unsigned long)sys_addr, (unsigned long)input_addr); | ||
| 559 | |||
| 560 | return input_addr; | ||
| 561 | } | ||
| 562 | |||
| 563 | |||
| 564 | /* | ||
| 565 | * @input_addr is an InputAddr associated with the node represented by mci. | ||
| 566 | * Translate @input_addr to a DramAddr and return the result. | ||
| 567 | */ | ||
| 568 | static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr) | ||
| 569 | { | ||
| 570 | struct amd64_pvt *pvt; | ||
| 571 | int node_id, intlv_shift; | ||
| 572 | u64 bits, dram_addr; | ||
| 573 | u32 intlv_sel; | ||
| 574 | |||
| 575 | /* | ||
| 576 | * Near the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E) | ||
| 577 | * shows how to translate a DramAddr to an InputAddr. Here we reverse | ||
| 578 | * this procedure. When translating from a DramAddr to an InputAddr, the | ||
| 579 | * bits used for node interleaving are discarded. Here we recover these | ||
| 580 | * bits from the IntlvSel field of the DRAM Limit register (section | ||
| 581 | * 3.4.4.2) for the node that input_addr is associated with. | ||
| 582 | */ | ||
| 583 | pvt = mci->pvt_info; | ||
| 584 | node_id = pvt->mc_node_id; | ||
| 585 | BUG_ON((node_id < 0) || (node_id > 7)); | ||
| 586 | |||
| 587 | intlv_shift = num_node_interleave_bits(pvt->dram_IntlvEn[0]); | ||
| 588 | |||
| 589 | if (intlv_shift == 0) { | ||
| 590 | debugf1(" InputAddr 0x%lx translates to DramAddr of " | ||
| 591 | "same value\n", (unsigned long)input_addr); | ||
| 592 | |||
| 593 | return input_addr; | ||
| 594 | } | ||
| 595 | |||
| 596 | bits = ((input_addr & 0xffffff000ull) << intlv_shift) + | ||
| 597 | (input_addr & 0xfff); | ||
| 598 | |||
| 599 | intlv_sel = pvt->dram_IntlvSel[node_id] & ((1 << intlv_shift) - 1); | ||
| 600 | dram_addr = bits + (intlv_sel << 12); | ||
| 601 | |||
| 602 | debugf1("InputAddr 0x%lx translates to DramAddr 0x%lx " | ||
| 603 | "(%d node interleave bits)\n", (unsigned long)input_addr, | ||
| 604 | (unsigned long)dram_addr, intlv_shift); | ||
| 605 | |||
| 606 | return dram_addr; | ||
| 607 | } | ||
| 608 | |||
| 609 | /* | ||
| 610 | * @dram_addr is a DramAddr that maps to the node represented by mci. Convert | ||
| 611 | * @dram_addr to a SysAddr. | ||
| 612 | */ | ||
| 613 | static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr) | ||
| 614 | { | ||
| 615 | struct amd64_pvt *pvt = mci->pvt_info; | ||
| 616 | u64 hole_base, hole_offset, hole_size, base, limit, sys_addr; | ||
| 617 | int ret = 0; | ||
| 618 | |||
| 619 | ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, | ||
| 620 | &hole_size); | ||
| 621 | if (!ret) { | ||
| 622 | if ((dram_addr >= hole_base) && | ||
| 623 | (dram_addr < (hole_base + hole_size))) { | ||
| 624 | sys_addr = dram_addr + hole_offset; | ||
| 625 | |||
| 626 | debugf1("using DHAR to translate DramAddr 0x%lx to " | ||
| 627 | "SysAddr 0x%lx\n", (unsigned long)dram_addr, | ||
| 628 | (unsigned long)sys_addr); | ||
| 629 | |||
| 630 | return sys_addr; | ||
| 631 | } | ||
| 632 | } | ||
| 633 | |||
| 634 | amd64_get_base_and_limit(pvt, pvt->mc_node_id, &base, &limit); | ||
| 635 | sys_addr = dram_addr + base; | ||
| 636 | |||
| 637 | /* | ||
| 638 | * The sys_addr we have computed up to this point is a 40-bit value | ||
| 639 | * because the k8 deals with 40-bit values. However, the value we are | ||
| 640 | * supposed to return is a full 64-bit physical address. The AMD | ||
| 641 | * x86-64 architecture specifies that the most significant implemented | ||
| 642 | * address bit through bit 63 of a physical address must be either all | ||
| 643 | * 0s or all 1s. Therefore we sign-extend the 40-bit sys_addr to a | ||
| 644 | * 64-bit value below. See section 3.4.2 of AMD publication 24592: | ||
| 645 | * AMD x86-64 Architecture Programmer's Manual Volume 1 Application | ||
| 646 | * Programming. | ||
| 647 | */ | ||
| 648 | sys_addr |= ~((sys_addr & (1ull << 39)) - 1); | ||
| 649 | |||
| 650 | debugf1(" Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n", | ||
| 651 | pvt->mc_node_id, (unsigned long)dram_addr, | ||
| 652 | (unsigned long)sys_addr); | ||
| 653 | |||
| 654 | return sys_addr; | ||
| 655 | } | ||
| 656 | |||
| 657 | /* | ||
| 658 | * @input_addr is an InputAddr associated with the node given by mci. Translate | ||
| 659 | * @input_addr to a SysAddr. | ||
| 660 | */ | ||
| 661 | static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci, | ||
| 662 | u64 input_addr) | ||
| 663 | { | ||
| 664 | return dram_addr_to_sys_addr(mci, | ||
| 665 | input_addr_to_dram_addr(mci, input_addr)); | ||
| 666 | } | ||
| 667 | |||
| 668 | /* | ||
| 669 | * Find the minimum and maximum InputAddr values that map to the given @csrow. | ||
| 670 | * Pass back these values in *input_addr_min and *input_addr_max. | ||
| 671 | */ | ||
| 672 | static void find_csrow_limits(struct mem_ctl_info *mci, int csrow, | ||
| 673 | u64 *input_addr_min, u64 *input_addr_max) | ||
| 674 | { | ||
| 675 | struct amd64_pvt *pvt; | ||
| 676 | u64 base, mask; | ||
| 677 | |||
| 678 | pvt = mci->pvt_info; | ||
| 679 | BUG_ON((csrow < 0) || (csrow >= CHIPSELECT_COUNT)); | ||
| 680 | |||
| 681 | base = base_from_dct_base(pvt, csrow); | ||
| 682 | mask = mask_from_dct_mask(pvt, csrow); | ||
| 683 | |||
| 684 | *input_addr_min = base & ~mask; | ||
| 685 | *input_addr_max = base | mask | pvt->dcs_mask_notused; | ||
| 686 | } | ||
| 687 | |||
| 688 | /* | ||
| 689 | * Extract error address from MCA NB Address Low (section 3.6.4.5) and MCA NB | ||
| 690 | * Address High (section 3.6.4.6) register values and return the result. Address | ||
| 691 | * is located in the info structure (nbeah and nbeal), the encoding is device | ||
| 692 | * specific. | ||
| 693 | */ | ||
| 694 | static u64 extract_error_address(struct mem_ctl_info *mci, | ||
| 695 | struct amd64_error_info_regs *info) | ||
| 696 | { | ||
| 697 | struct amd64_pvt *pvt = mci->pvt_info; | ||
| 698 | |||
| 699 | return pvt->ops->get_error_address(mci, info); | ||
| 700 | } | ||
| 701 | |||
| 702 | |||
| 703 | /* Map the Error address to a PAGE and PAGE OFFSET. */ | ||
| 704 | static inline void error_address_to_page_and_offset(u64 error_address, | ||
| 705 | u32 *page, u32 *offset) | ||
| 706 | { | ||
| 707 | *page = (u32) (error_address >> PAGE_SHIFT); | ||
| 708 | *offset = ((u32) error_address) & ~PAGE_MASK; | ||
| 709 | } | ||
| 710 | |||
| 711 | /* | ||
| 712 | * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address | ||
| 713 | * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers | ||
| 714 | * of a node that detected an ECC memory error. mci represents the node that | ||
| 715 | * the error address maps to (possibly different from the node that detected | ||
| 716 | * the error). Return the number of the csrow that sys_addr maps to, or -1 on | ||
| 717 | * error. | ||
| 718 | */ | ||
| 719 | static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr) | ||
| 720 | { | ||
| 721 | int csrow; | ||
| 722 | |||
| 723 | csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr)); | ||
| 724 | |||
| 725 | if (csrow == -1) | ||
| 726 | amd64_mc_printk(mci, KERN_ERR, | ||
| 727 | "Failed to translate InputAddr to csrow for " | ||
| 728 | "address 0x%lx\n", (unsigned long)sys_addr); | ||
| 729 | return csrow; | ||
| 730 | } | ||
| 437 | 731 | ||
