linux/arch/x86/pci/mmconfig-shared.c
<<
>>
Prefs
   1/*
   2 * mmconfig-shared.c - Low-level direct PCI config space access via
   3 *                     MMCONFIG - common code between i386 and x86-64.
   4 *
   5 * This code does:
   6 * - known chipset handling
   7 * - ACPI decoding and validation
   8 *
   9 * Per-architecture code takes care of the mappings and accesses
  10 * themselves.
  11 */
  12
  13#include <linux/pci.h>
  14#include <linux/init.h>
  15#include <linux/acpi.h>
  16#include <linux/sfi_acpi.h>
  17#include <linux/bitmap.h>
  18#include <linux/dmi.h>
  19#include <linux/slab.h>
  20#include <asm/e820.h>
  21#include <asm/pci_x86.h>
  22#include <asm/acpi.h>
  23
  24#define PREFIX "PCI: "
  25
  26/* Indicate if the mmcfg resources have been placed into the resource table. */
  27static int __initdata pci_mmcfg_resources_inserted;
  28
  29LIST_HEAD(pci_mmcfg_list);
  30
  31static __init void pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
  32{
  33        if (cfg->res.parent)
  34                release_resource(&cfg->res);
  35        list_del(&cfg->list);
  36        kfree(cfg);
  37}
  38
  39static __init void free_all_mmcfg(void)
  40{
  41        struct pci_mmcfg_region *cfg, *tmp;
  42
  43        pci_mmcfg_arch_free();
  44        list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list)
  45                pci_mmconfig_remove(cfg);
  46}
  47
  48static __init void list_add_sorted(struct pci_mmcfg_region *new)
  49{
  50        struct pci_mmcfg_region *cfg;
  51
  52        /* keep list sorted by segment and starting bus number */
  53        list_for_each_entry(cfg, &pci_mmcfg_list, list) {
  54                if (cfg->segment > new->segment ||
  55                    (cfg->segment == new->segment &&
  56                     cfg->start_bus >= new->start_bus)) {
  57                        list_add_tail(&new->list, &cfg->list);
  58                        return;
  59                }
  60        }
  61        list_add_tail(&new->list, &pci_mmcfg_list);
  62}
  63
  64static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
  65                                                        int end, u64 addr)
  66{
  67        struct pci_mmcfg_region *new;
  68        struct resource *res;
  69
  70        if (addr == 0)
  71                return NULL;
  72
  73        new = kzalloc(sizeof(*new), GFP_KERNEL);
  74        if (!new)
  75                return NULL;
  76
  77        new->address = addr;
  78        new->segment = segment;
  79        new->start_bus = start;
  80        new->end_bus = end;
  81
  82        list_add_sorted(new);
  83
  84        res = &new->res;
  85        res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
  86        res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
  87        res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
  88        snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
  89                 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
  90        res->name = new->name;
  91
  92        printk(KERN_INFO PREFIX "MMCONFIG for domain %04x [bus %02x-%02x] at "
  93               "%pR (base %#lx)\n", segment, start, end, &new->res,
  94               (unsigned long) addr);
  95
  96        return new;
  97}
  98
  99struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
 100{
 101        struct pci_mmcfg_region *cfg;
 102
 103        list_for_each_entry(cfg, &pci_mmcfg_list, list)
 104                if (cfg->segment == segment &&
 105                    cfg->start_bus <= bus && bus <= cfg->end_bus)
 106                        return cfg;
 107
 108        return NULL;
 109}
 110
 111static const char __init *pci_mmcfg_e7520(void)
 112{
 113        u32 win;
 114        raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win);
 115
 116        win = win & 0xf000;
 117        if (win == 0x0000 || win == 0xf000)
 118                return NULL;
 119
 120        if (pci_mmconfig_add(0, 0, 255, win << 16) == NULL)
 121                return NULL;
 122
 123        return "Intel Corporation E7520 Memory Controller Hub";
 124}
 125
 126static const char __init *pci_mmcfg_intel_945(void)
 127{
 128        u32 pciexbar, mask = 0, len = 0;
 129
 130        raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar);
 131
 132        /* Enable bit */
 133        if (!(pciexbar & 1))
 134                return NULL;
 135
 136        /* Size bits */
 137        switch ((pciexbar >> 1) & 3) {
 138        case 0:
 139                mask = 0xf0000000U;
 140                len  = 0x10000000U;
 141                break;
 142        case 1:
 143                mask = 0xf8000000U;
 144                len  = 0x08000000U;
 145                break;
 146        case 2:
 147                mask = 0xfc000000U;
 148                len  = 0x04000000U;
 149                break;
 150        default:
 151                return NULL;
 152        }
 153
 154        /* Errata #2, things break when not aligned on a 256Mb boundary */
 155        /* Can only happen in 64M/128M mode */
 156
 157        if ((pciexbar & mask) & 0x0fffffffU)
 158                return NULL;
 159
 160        /* Don't hit the APIC registers and their friends */
 161        if ((pciexbar & mask) >= 0xf0000000U)
 162                return NULL;
 163
 164        if (pci_mmconfig_add(0, 0, (len >> 20) - 1, pciexbar & mask) == NULL)
 165                return NULL;
 166
 167        return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
 168}
 169
 170static const char __init *pci_mmcfg_amd_fam10h(void)
 171{
 172        u32 low, high, address;
 173        u64 base, msr;
 174        int i;
 175        unsigned segnbits = 0, busnbits, end_bus;
 176
 177        if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF))
 178                return NULL;
 179
 180        address = MSR_FAM10H_MMIO_CONF_BASE;
 181        if (rdmsr_safe(address, &low, &high))
 182                return NULL;
 183
 184        msr = high;
 185        msr <<= 32;
 186        msr |= low;
 187
 188        /* mmconfig is not enable */
 189        if (!(msr & FAM10H_MMIO_CONF_ENABLE))
 190                return NULL;
 191
 192        base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT);
 193
 194        busnbits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
 195                         FAM10H_MMIO_CONF_BUSRANGE_MASK;
 196
 197        /*
 198         * only handle bus 0 ?
 199         * need to skip it
 200         */
 201        if (!busnbits)
 202                return NULL;
 203
 204        if (busnbits > 8) {
 205                segnbits = busnbits - 8;
 206                busnbits = 8;
 207        }
 208
 209        end_bus = (1 << busnbits) - 1;
 210        for (i = 0; i < (1 << segnbits); i++)
 211                if (pci_mmconfig_add(i, 0, end_bus,
 212                                     base + (1<<28) * i) == NULL) {
 213                        free_all_mmcfg();
 214                        return NULL;
 215                }
 216
 217        return "AMD Family 10h NB";
 218}
 219
 220static bool __initdata mcp55_checked;
 221static const char __init *pci_mmcfg_nvidia_mcp55(void)
 222{
 223        int bus;
 224        int mcp55_mmconf_found = 0;
 225
 226        static const u32 extcfg_regnum          = 0x90;
 227        static const u32 extcfg_regsize         = 4;
 228        static const u32 extcfg_enable_mask     = 1<<31;
 229        static const u32 extcfg_start_mask      = 0xff<<16;
 230        static const int extcfg_start_shift     = 16;
 231        static const u32 extcfg_size_mask       = 0x3<<28;
 232        static const int extcfg_size_shift      = 28;
 233        static const int extcfg_sizebus[]       = {0x100, 0x80, 0x40, 0x20};
 234        static const u32 extcfg_base_mask[]     = {0x7ff8, 0x7ffc, 0x7ffe, 0x7fff};
 235        static const int extcfg_base_lshift     = 25;
 236
 237        /*
 238         * do check if amd fam10h already took over
 239         */
 240        if (!acpi_disabled || !list_empty(&pci_mmcfg_list) || mcp55_checked)
 241                return NULL;
 242
 243        mcp55_checked = true;
 244        for (bus = 0; bus < 256; bus++) {
 245                u64 base;
 246                u32 l, extcfg;
 247                u16 vendor, device;
 248                int start, size_index, end;
 249
 250                raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), 0, 4, &l);
 251                vendor = l & 0xffff;
 252                device = (l >> 16) & 0xffff;
 253
 254                if (PCI_VENDOR_ID_NVIDIA != vendor || 0x0369 != device)
 255                        continue;
 256
 257                raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), extcfg_regnum,
 258                                  extcfg_regsize, &extcfg);
 259
 260                if (!(extcfg & extcfg_enable_mask))
 261                        continue;
 262
 263                size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift;
 264                base = extcfg & extcfg_base_mask[size_index];
 265                /* base could > 4G */
 266                base <<= extcfg_base_lshift;
 267                start = (extcfg & extcfg_start_mask) >> extcfg_start_shift;
 268                end = start + extcfg_sizebus[size_index] - 1;
 269                if (pci_mmconfig_add(0, start, end, base) == NULL)
 270                        continue;
 271                mcp55_mmconf_found++;
 272        }
 273
 274        if (!mcp55_mmconf_found)
 275                return NULL;
 276
 277        return "nVidia MCP55";
 278}
 279
 280struct pci_mmcfg_hostbridge_probe {
 281        u32 bus;
 282        u32 devfn;
 283        u32 vendor;
 284        u32 device;
 285        const char *(*probe)(void);
 286};
 287
 288static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = {
 289        { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
 290          PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 },
 291        { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
 292          PCI_DEVICE_ID_INTEL_82945G_HB, pci_mmcfg_intel_945 },
 293        { 0, PCI_DEVFN(0x18, 0), PCI_VENDOR_ID_AMD,
 294          0x1200, pci_mmcfg_amd_fam10h },
 295        { 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD,
 296          0x1200, pci_mmcfg_amd_fam10h },
 297        { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_NVIDIA,
 298          0x0369, pci_mmcfg_nvidia_mcp55 },
 299};
 300
 301static void __init pci_mmcfg_check_end_bus_number(void)
 302{
 303        struct pci_mmcfg_region *cfg, *cfgx;
 304
 305        /* Fixup overlaps */
 306        list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 307                if (cfg->end_bus < cfg->start_bus)
 308                        cfg->end_bus = 255;
 309
 310                /* Don't access the list head ! */
 311                if (cfg->list.next == &pci_mmcfg_list)
 312                        break;
 313
 314                cfgx = list_entry(cfg->list.next, typeof(*cfg), list);
 315                if (cfg->end_bus >= cfgx->start_bus)
 316                        cfg->end_bus = cfgx->start_bus - 1;
 317        }
 318}
 319
 320static int __init pci_mmcfg_check_hostbridge(void)
 321{
 322        u32 l;
 323        u32 bus, devfn;
 324        u16 vendor, device;
 325        int i;
 326        const char *name;
 327
 328        if (!raw_pci_ops)
 329                return 0;
 330
 331        free_all_mmcfg();
 332
 333        for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
 334                bus =  pci_mmcfg_probes[i].bus;
 335                devfn = pci_mmcfg_probes[i].devfn;
 336                raw_pci_ops->read(0, bus, devfn, 0, 4, &l);
 337                vendor = l & 0xffff;
 338                device = (l >> 16) & 0xffff;
 339
 340                name = NULL;
 341                if (pci_mmcfg_probes[i].vendor == vendor &&
 342                    pci_mmcfg_probes[i].device == device)
 343                        name = pci_mmcfg_probes[i].probe();
 344
 345                if (name)
 346                        printk(KERN_INFO PREFIX "%s with MMCONFIG support\n",
 347                               name);
 348        }
 349
 350        /* some end_bus_number is crazy, fix it */
 351        pci_mmcfg_check_end_bus_number();
 352
 353        return !list_empty(&pci_mmcfg_list);
 354}
 355
 356static void __init pci_mmcfg_insert_resources(void)
 357{
 358        struct pci_mmcfg_region *cfg;
 359
 360        list_for_each_entry(cfg, &pci_mmcfg_list, list)
 361                insert_resource(&iomem_resource, &cfg->res);
 362
 363        /* Mark that the resources have been inserted. */
 364        pci_mmcfg_resources_inserted = 1;
 365}
 366
 367static acpi_status __init check_mcfg_resource(struct acpi_resource *res,
 368                                              void *data)
 369{
 370        struct resource *mcfg_res = data;
 371        struct acpi_resource_address64 address;
 372        acpi_status status;
 373
 374        if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
 375                struct acpi_resource_fixed_memory32 *fixmem32 =
 376                        &res->data.fixed_memory32;
 377                if (!fixmem32)
 378                        return AE_OK;
 379                if ((mcfg_res->start >= fixmem32->address) &&
 380                    (mcfg_res->end < (fixmem32->address +
 381                                      fixmem32->address_length))) {
 382                        mcfg_res->flags = 1;
 383                        return AE_CTRL_TERMINATE;
 384                }
 385        }
 386        if ((res->type != ACPI_RESOURCE_TYPE_ADDRESS32) &&
 387            (res->type != ACPI_RESOURCE_TYPE_ADDRESS64))
 388                return AE_OK;
 389
 390        status = acpi_resource_to_address64(res, &address);
 391        if (ACPI_FAILURE(status) ||
 392           (address.address_length <= 0) ||
 393           (address.resource_type != ACPI_MEMORY_RANGE))
 394                return AE_OK;
 395
 396        if ((mcfg_res->start >= address.minimum) &&
 397            (mcfg_res->end < (address.minimum + address.address_length))) {
 398                mcfg_res->flags = 1;
 399                return AE_CTRL_TERMINATE;
 400        }
 401        return AE_OK;
 402}
 403
 404static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl,
 405                void *context, void **rv)
 406{
 407        struct resource *mcfg_res = context;
 408
 409        acpi_walk_resources(handle, METHOD_NAME__CRS,
 410                            check_mcfg_resource, context);
 411
 412        if (mcfg_res->flags)
 413                return AE_CTRL_TERMINATE;
 414
 415        return AE_OK;
 416}
 417
 418static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used)
 419{
 420        struct resource mcfg_res;
 421
 422        mcfg_res.start = start;
 423        mcfg_res.end = end - 1;
 424        mcfg_res.flags = 0;
 425
 426        acpi_get_devices("PNP0C01", find_mboard_resource, &mcfg_res, NULL);
 427
 428        if (!mcfg_res.flags)
 429                acpi_get_devices("PNP0C02", find_mboard_resource, &mcfg_res,
 430                                 NULL);
 431
 432        return mcfg_res.flags;
 433}
 434
 435typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type);
 436
 437static int __init is_mmconf_reserved(check_reserved_t is_reserved,
 438                                    struct pci_mmcfg_region *cfg, int with_e820)
 439{
 440        u64 addr = cfg->res.start;
 441        u64 size = resource_size(&cfg->res);
 442        u64 old_size = size;
 443        int valid = 0, num_buses;
 444
 445        while (!is_reserved(addr, addr + size, E820_RESERVED)) {
 446                size >>= 1;
 447                if (size < (16UL<<20))
 448                        break;
 449        }
 450
 451        if (size >= (16UL<<20) || size == old_size) {
 452                printk(KERN_INFO PREFIX "MMCONFIG at %pR reserved in %s\n",
 453                       &cfg->res,
 454                       with_e820 ? "E820" : "ACPI motherboard resources");
 455                valid = 1;
 456
 457                if (old_size != size) {
 458                        /* update end_bus */
 459                        cfg->end_bus = cfg->start_bus + ((size>>20) - 1);
 460                        num_buses = cfg->end_bus - cfg->start_bus + 1;
 461                        cfg->res.end = cfg->res.start +
 462                            PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
 463                        snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
 464                                 "PCI MMCONFIG %04x [bus %02x-%02x]",
 465                                 cfg->segment, cfg->start_bus, cfg->end_bus);
 466                        printk(KERN_INFO PREFIX
 467                               "MMCONFIG for %04x [bus%02x-%02x] "
 468                               "at %pR (base %#lx) (size reduced!)\n",
 469                               cfg->segment, cfg->start_bus, cfg->end_bus,
 470                               &cfg->res, (unsigned long) cfg->address);
 471                }
 472        }
 473
 474        return valid;
 475}
 476
 477static void __init pci_mmcfg_reject_broken(int early)
 478{
 479        struct pci_mmcfg_region *cfg;
 480
 481        list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 482                int valid = 0;
 483
 484                if (!early && !acpi_disabled) {
 485                        valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0);
 486
 487                        if (valid)
 488                                continue;
 489                        else
 490                                printk(KERN_ERR FW_BUG PREFIX
 491                                       "MMCONFIG at %pR not reserved in "
 492                                       "ACPI motherboard resources\n",
 493                                       &cfg->res);
 494                }
 495
 496                /* Don't try to do this check unless configuration
 497                   type 1 is available. how about type 2 ?*/
 498                if (raw_pci_ops)
 499                        valid = is_mmconf_reserved(e820_all_mapped, cfg, 1);
 500
 501                if (!valid)
 502                        goto reject;
 503        }
 504
 505        return;
 506
 507reject:
 508        printk(KERN_INFO PREFIX "not using MMCONFIG\n");
 509        free_all_mmcfg();
 510}
 511
 512static int __initdata known_bridge;
 513
 514static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
 515                                        struct acpi_mcfg_allocation *cfg)
 516{
 517        int year;
 518
 519        if (cfg->address < 0xFFFFFFFF)
 520                return 0;
 521
 522        if (!strcmp(mcfg->header.oem_id, "SGI") ||
 523                        !strcmp(mcfg->header.oem_id, "SGI2"))
 524                return 0;
 525
 526        if (mcfg->header.revision >= 1) {
 527                if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
 528                    year >= 2010)
 529                        return 0;
 530        }
 531
 532        printk(KERN_ERR PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
 533               "is above 4GB, ignored\n", cfg->pci_segment,
 534               cfg->start_bus_number, cfg->end_bus_number, cfg->address);
 535        return -EINVAL;
 536}
 537
 538static int __init pci_parse_mcfg(struct acpi_table_header *header)
 539{
 540        struct acpi_table_mcfg *mcfg;
 541        struct acpi_mcfg_allocation *cfg_table, *cfg;
 542        unsigned long i;
 543        int entries;
 544
 545        if (!header)
 546                return -EINVAL;
 547
 548        mcfg = (struct acpi_table_mcfg *)header;
 549
 550        /* how many config structures do we have */
 551        free_all_mmcfg();
 552        entries = 0;
 553        i = header->length - sizeof(struct acpi_table_mcfg);
 554        while (i >= sizeof(struct acpi_mcfg_allocation)) {
 555                entries++;
 556                i -= sizeof(struct acpi_mcfg_allocation);
 557        };
 558        if (entries == 0) {
 559                printk(KERN_ERR PREFIX "MMCONFIG has no entries\n");
 560                return -ENODEV;
 561        }
 562
 563        cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
 564        for (i = 0; i < entries; i++) {
 565                cfg = &cfg_table[i];
 566                if (acpi_mcfg_check_entry(mcfg, cfg)) {
 567                        free_all_mmcfg();
 568                        return -ENODEV;
 569                }
 570
 571                if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
 572                                   cfg->end_bus_number, cfg->address) == NULL) {
 573                        printk(KERN_WARNING PREFIX
 574                               "no memory for MCFG entries\n");
 575                        free_all_mmcfg();
 576                        return -ENOMEM;
 577                }
 578        }
 579
 580        return 0;
 581}
 582
 583static void __init __pci_mmcfg_init(int early)
 584{
 585        /* MMCONFIG disabled */
 586        if ((pci_probe & PCI_PROBE_MMCONF) == 0)
 587                return;
 588
 589        /* MMCONFIG already enabled */
 590        if (!early && !(pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF))
 591                return;
 592
 593        /* for late to exit */
 594        if (known_bridge)
 595                return;
 596
 597        if (early) {
 598                if (pci_mmcfg_check_hostbridge())
 599                        known_bridge = 1;
 600        }
 601
 602        if (!known_bridge)
 603                acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
 604
 605        pci_mmcfg_reject_broken(early);
 606
 607        if (list_empty(&pci_mmcfg_list))
 608                return;
 609
 610        if (pcibios_last_bus < 0) {
 611                const struct pci_mmcfg_region *cfg;
 612
 613                list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 614                        if (cfg->segment)
 615                                break;
 616                        pcibios_last_bus = cfg->end_bus;
 617                }
 618        }
 619
 620        if (pci_mmcfg_arch_init())
 621                pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
 622        else {
 623                /*
 624                 * Signal not to attempt to insert mmcfg resources because
 625                 * the architecture mmcfg setup could not initialize.
 626                 */
 627                pci_mmcfg_resources_inserted = 1;
 628        }
 629}
 630
 631void __init pci_mmcfg_early_init(void)
 632{
 633        __pci_mmcfg_init(1);
 634}
 635
 636void __init pci_mmcfg_late_init(void)
 637{
 638        __pci_mmcfg_init(0);
 639}
 640
 641static int __init pci_mmcfg_late_insert_resources(void)
 642{
 643        /*
 644         * If resources are already inserted or we are not using MMCONFIG,
 645         * don't insert the resources.
 646         */
 647        if ((pci_mmcfg_resources_inserted == 1) ||
 648            (pci_probe & PCI_PROBE_MMCONF) == 0 ||
 649            list_empty(&pci_mmcfg_list))
 650                return 1;
 651
 652        /*
 653         * Attempt to insert the mmcfg resources but not with the busy flag
 654         * marked so it won't cause request errors when __request_region is
 655         * called.
 656         */
 657        pci_mmcfg_insert_resources();
 658
 659        return 0;
 660}
 661
 662/*
 663 * Perform MMCONFIG resource insertion after PCI initialization to allow for
 664 * misprogrammed MCFG tables that state larger sizes but actually conflict
 665 * with other system resources.
 666 */
 667late_initcall(pci_mmcfg_late_insert_resources);
 668