diff options
author | Tejun Heo <htejun@gmail.com> | 2007-12-15 01:05:06 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-12-17 20:33:15 -0500 |
commit | 3264a8d8f95348e05cc6ac1ce747a8339ed7ab08 (patch) | |
tree | 7d9045e726e7e63d6a7cad426b1c9f000298c886 /drivers/ata | |
parent | 0e8634bf8e48e50aa96c7e7becafcf9d98c1a28d (diff) |
libata-acpi: implement _GTF command filtering
Implement _GTF command filtering which can be controlled by
libata.acpi_filter kernel parameter. Currently SETXFER and LOCK
commands are filtered.
libata configures transfer mode by itself and _GTF SETXFER commands
can potentially disrupt device configuration. _GTM/_STM mechanism
can't handle hotplugging too well and when _GTF is executed,
controller is in PIO0 rather than the mode _STM configured.
Note that detecting SET MAX LOCK requires looking at the previous
command. This adds a bit to code complexity.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/libata-acpi.c | 159 |
1 files changed, 115 insertions, 44 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index d4cb557a727d..7bf4befd96bc 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Copyright (C) 2006 Randy Dunlap | 6 | * Copyright (C) 2006 Randy Dunlap |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/module.h> | ||
9 | #include <linux/ata.h> | 10 | #include <linux/ata.h> |
10 | #include <linux/delay.h> | 11 | #include <linux/delay.h> |
11 | #include <linux/device.h> | 12 | #include <linux/device.h> |
@@ -25,6 +26,18 @@ | |||
25 | #include <acpi/acmacros.h> | 26 | #include <acpi/acmacros.h> |
26 | #include <acpi/actypes.h> | 27 | #include <acpi/actypes.h> |
27 | 28 | ||
29 | enum { | ||
30 | ATA_ACPI_FILTER_SETXFER = 1 << 0, | ||
31 | ATA_ACPI_FILTER_LOCK = 1 << 1, | ||
32 | |||
33 | ATA_ACPI_FILTER_DEFAULT = ATA_ACPI_FILTER_SETXFER | | ||
34 | ATA_ACPI_FILTER_LOCK, | ||
35 | }; | ||
36 | |||
37 | static unsigned int ata_acpi_gtf_filter = ATA_ACPI_FILTER_DEFAULT; | ||
38 | module_param_named(acpi_gtf_filter, ata_acpi_gtf_filter, int, 0644); | ||
39 | MODULE_PARM_DESC(acpi_gtf_filter, "filter mask for ACPI _GTF commands, set to filter out (0x1=set xfermode, 0x2=lock/freeze lock)"); | ||
40 | |||
28 | #define NO_PORT_MULT 0xffff | 41 | #define NO_PORT_MULT 0xffff |
29 | #define SATA_ADR(root, pmp) (((root) << 16) | (pmp)) | 42 | #define SATA_ADR(root, pmp) (((root) << 16) | (pmp)) |
30 | 43 | ||
@@ -465,6 +478,60 @@ int ata_acpi_cbl_80wire(struct ata_port *ap) | |||
465 | 478 | ||
466 | EXPORT_SYMBOL_GPL(ata_acpi_cbl_80wire); | 479 | EXPORT_SYMBOL_GPL(ata_acpi_cbl_80wire); |
467 | 480 | ||
481 | static void ata_acpi_gtf_to_tf(struct ata_device *dev, | ||
482 | const struct ata_acpi_gtf *gtf, | ||
483 | struct ata_taskfile *tf) | ||
484 | { | ||
485 | ata_tf_init(dev, tf); | ||
486 | |||
487 | tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; | ||
488 | tf->protocol = ATA_PROT_NODATA; | ||
489 | tf->feature = gtf->tf[0]; /* 0x1f1 */ | ||
490 | tf->nsect = gtf->tf[1]; /* 0x1f2 */ | ||
491 | tf->lbal = gtf->tf[2]; /* 0x1f3 */ | ||
492 | tf->lbam = gtf->tf[3]; /* 0x1f4 */ | ||
493 | tf->lbah = gtf->tf[4]; /* 0x1f5 */ | ||
494 | tf->device = gtf->tf[5]; /* 0x1f6 */ | ||
495 | tf->command = gtf->tf[6]; /* 0x1f7 */ | ||
496 | } | ||
497 | |||
498 | static int ata_acpi_filter_tf(const struct ata_taskfile *tf, | ||
499 | const struct ata_taskfile *ptf) | ||
500 | { | ||
501 | if (ata_acpi_gtf_filter & ATA_ACPI_FILTER_SETXFER) { | ||
502 | /* libata doesn't use ACPI to configure transfer mode. | ||
503 | * It will only confuse device configuration. Skip. | ||
504 | */ | ||
505 | if (tf->command == ATA_CMD_SET_FEATURES && | ||
506 | tf->feature == SETFEATURES_XFER) | ||
507 | return 1; | ||
508 | } | ||
509 | |||
510 | if (ata_acpi_gtf_filter & ATA_ACPI_FILTER_LOCK) { | ||
511 | /* BIOS writers, sorry but we don't wanna lock | ||
512 | * features unless the user explicitly said so. | ||
513 | */ | ||
514 | |||
515 | /* DEVICE CONFIGURATION FREEZE LOCK */ | ||
516 | if (tf->command == ATA_CMD_CONF_OVERLAY && | ||
517 | tf->feature == ATA_DCO_FREEZE_LOCK) | ||
518 | return 1; | ||
519 | |||
520 | /* SECURITY FREEZE LOCK */ | ||
521 | if (tf->command == ATA_CMD_SEC_FREEZE_LOCK) | ||
522 | return 1; | ||
523 | |||
524 | /* SET MAX LOCK and SET MAX FREEZE LOCK */ | ||
525 | if ((!ptf || ptf->command != ATA_CMD_READ_NATIVE_MAX) && | ||
526 | tf->command == ATA_CMD_SET_MAX && | ||
527 | (tf->feature == ATA_SET_MAX_LOCK || | ||
528 | tf->feature == ATA_SET_MAX_FREEZE_LOCK)) | ||
529 | return 1; | ||
530 | } | ||
531 | |||
532 | return 0; | ||
533 | } | ||
534 | |||
468 | /** | 535 | /** |
469 | * ata_acpi_run_tf - send taskfile registers to host controller | 536 | * ata_acpi_run_tf - send taskfile registers to host controller |
470 | * @dev: target ATA device | 537 | * @dev: target ATA device |
@@ -485,13 +552,15 @@ EXPORT_SYMBOL_GPL(ata_acpi_cbl_80wire); | |||
485 | * EH context. | 552 | * EH context. |
486 | * | 553 | * |
487 | * RETURNS: | 554 | * RETURNS: |
488 | * 1 if command is executed successfully. 0 if ignored or rejected, | 555 | * 1 if command is executed successfully. 0 if ignored, rejected or |
489 | * -errno on other errors. | 556 | * filtered out, -errno on other errors. |
490 | */ | 557 | */ |
491 | static int ata_acpi_run_tf(struct ata_device *dev, | 558 | static int ata_acpi_run_tf(struct ata_device *dev, |
492 | const struct ata_acpi_gtf *gtf) | 559 | const struct ata_acpi_gtf *gtf, |
560 | const struct ata_acpi_gtf *prev_gtf) | ||
493 | { | 561 | { |
494 | struct ata_taskfile tf, rtf; | 562 | struct ata_taskfile *pptf = NULL; |
563 | struct ata_taskfile tf, ptf, rtf; | ||
495 | unsigned int err_mask; | 564 | unsigned int err_mask; |
496 | const char *level; | 565 | const char *level; |
497 | char msg[60]; | 566 | char msg[60]; |
@@ -502,44 +571,44 @@ static int ata_acpi_run_tf(struct ata_device *dev, | |||
502 | && (gtf->tf[6] == 0)) | 571 | && (gtf->tf[6] == 0)) |
503 | return 0; | 572 | return 0; |
504 | 573 | ||
505 | ata_tf_init(dev, &tf); | 574 | ata_acpi_gtf_to_tf(dev, gtf, &tf); |
506 | 575 | if (prev_gtf) { | |
507 | /* convert gtf to tf */ | 576 | ata_acpi_gtf_to_tf(dev, prev_gtf, &ptf); |
508 | tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; /* TBD */ | 577 | pptf = &ptf; |
509 | tf.protocol = ATA_PROT_NODATA; | 578 | } |
510 | tf.feature = gtf->tf[0]; /* 0x1f1 */ | 579 | |
511 | tf.nsect = gtf->tf[1]; /* 0x1f2 */ | 580 | if (!ata_acpi_filter_tf(&tf, pptf)) { |
512 | tf.lbal = gtf->tf[2]; /* 0x1f3 */ | 581 | rtf = tf; |
513 | tf.lbam = gtf->tf[3]; /* 0x1f4 */ | 582 | err_mask = ata_exec_internal(dev, &rtf, NULL, |
514 | tf.lbah = gtf->tf[4]; /* 0x1f5 */ | 583 | DMA_NONE, NULL, 0, 0); |
515 | tf.device = gtf->tf[5]; /* 0x1f6 */ | 584 | |
516 | tf.command = gtf->tf[6]; /* 0x1f7 */ | 585 | switch (err_mask) { |
517 | 586 | case 0: | |
518 | rtf = tf; | 587 | level = KERN_DEBUG; |
519 | err_mask = ata_exec_internal(dev, &rtf, NULL, DMA_NONE, NULL, 0, 0); | 588 | snprintf(msg, sizeof(msg), "succeeded"); |
520 | 589 | rc = 1; | |
521 | switch (err_mask) { | 590 | break; |
522 | case 0: | 591 | |
523 | level = KERN_DEBUG; | 592 | case AC_ERR_DEV: |
524 | snprintf(msg, sizeof(msg), "succeeded"); | 593 | level = KERN_INFO; |
525 | rc = 1; | 594 | snprintf(msg, sizeof(msg), |
526 | break; | 595 | "rejected by device (Stat=0x%02x Err=0x%02x)", |
527 | 596 | rtf.command, rtf.feature); | |
528 | case AC_ERR_DEV: | 597 | rc = 0; |
598 | break; | ||
599 | |||
600 | default: | ||
601 | level = KERN_ERR; | ||
602 | snprintf(msg, sizeof(msg), | ||
603 | "failed (Emask=0x%x Stat=0x%02x Err=0x%02x)", | ||
604 | err_mask, rtf.command, rtf.feature); | ||
605 | rc = -EIO; | ||
606 | break; | ||
607 | } | ||
608 | } else { | ||
529 | level = KERN_INFO; | 609 | level = KERN_INFO; |
530 | snprintf(msg, sizeof(msg), | 610 | snprintf(msg, sizeof(msg), "filtered out"); |
531 | "rejected by device (Stat=0x%02x Err=0x%02x)", | ||
532 | rtf.command, rtf.feature); | ||
533 | rc = 0; | 611 | rc = 0; |
534 | break; | ||
535 | |||
536 | default: | ||
537 | level = KERN_ERR; | ||
538 | snprintf(msg, sizeof(msg), | ||
539 | "failed (Emask=0x%x Stat=0x%02x Err=0x%02x)", | ||
540 | err_mask, rtf.command, rtf.feature); | ||
541 | rc = -EIO; | ||
542 | break; | ||
543 | } | 612 | } |
544 | 613 | ||
545 | ata_dev_printk(dev, level, | 614 | ata_dev_printk(dev, level, |
@@ -566,7 +635,7 @@ static int ata_acpi_run_tf(struct ata_device *dev, | |||
566 | */ | 635 | */ |
567 | static int ata_acpi_exec_tfs(struct ata_device *dev, int *nr_executed) | 636 | static int ata_acpi_exec_tfs(struct ata_device *dev, int *nr_executed) |
568 | { | 637 | { |
569 | struct ata_acpi_gtf *gtf = NULL; | 638 | struct ata_acpi_gtf *gtf = NULL, *pgtf = NULL; |
570 | int gtf_count, i, rc; | 639 | int gtf_count, i, rc; |
571 | 640 | ||
572 | /* get taskfiles */ | 641 | /* get taskfiles */ |
@@ -576,12 +645,14 @@ static int ata_acpi_exec_tfs(struct ata_device *dev, int *nr_executed) | |||
576 | gtf_count = rc; | 645 | gtf_count = rc; |
577 | 646 | ||
578 | /* execute them */ | 647 | /* execute them */ |
579 | for (i = 0; i < gtf_count; i++) { | 648 | for (i = 0; i < gtf_count; i++, gtf++) { |
580 | rc = ata_acpi_run_tf(dev, gtf++); | 649 | rc = ata_acpi_run_tf(dev, gtf, pgtf); |
581 | if (rc < 0) | 650 | if (rc < 0) |
582 | break; | 651 | break; |
583 | if (rc) | 652 | if (rc) { |
584 | (*nr_executed)++; | 653 | (*nr_executed)++; |
654 | pgtf = gtf; | ||
655 | } | ||
585 | } | 656 | } |
586 | 657 | ||
587 | ata_acpi_clear_gtf(dev); | 658 | ata_acpi_clear_gtf(dev); |