/* * apei-io.c - APEI IO memory pre-mapping/post-unmapping and access * * Copyright (C) 2009-2010, Intel Corp. * Author: Huang Ying * Ported by: Liu, Jinsong * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static LIST_HEAD(apei_iomaps); /* * Used for mutual exclusion between writers of apei_iomaps list, for * synchronization between readers and writer. */ static DEFINE_SPINLOCK(apei_iomaps_lock); struct apei_iomap { struct list_head list; void __iomem *vaddr; unsigned long size; paddr_t paddr; }; static struct apei_iomap *__apei_find_iomap(paddr_t paddr, unsigned long size) { struct apei_iomap *map; list_for_each_entry(map, &apei_iomaps, list) { if (map->paddr + map->size >= paddr + size && map->paddr <= paddr) return map; } return NULL; } static void __iomem *__apei_ioremap_fast(paddr_t paddr, unsigned long size) { struct apei_iomap *map; map = __apei_find_iomap(paddr, size); if (map) return map->vaddr + (paddr - map->paddr); else return NULL; } static int apei_range_nr; static void __iomem *__init apei_range_map(paddr_t paddr, unsigned long size) { int i, pg; int start_nr, cur_nr; pg = ((((paddr + size -1) & PAGE_MASK) - (paddr & PAGE_MASK)) >> PAGE_SHIFT) + 1; if (apei_range_nr + pg > FIX_APEI_RANGE_MAX) return NULL; start_nr = apei_range_nr + pg -1; for (i = 0; i < pg; i++) { cur_nr = start_nr - i; set_fixmap_nocache(FIX_APEI_RANGE_BASE + cur_nr, paddr + (i << PAGE_SHIFT)); apei_range_nr++; } return (void __iomem *)fix_to_virt(FIX_APEI_RANGE_BASE + start_nr); } /* * Used to pre-map the specified IO memory area. First try to find * whether the area is already pre-mapped, if it is, return; otherwise, * do the real map, and add the mapping into apei_iomaps list. */ void __iomem *__init apei_pre_map(paddr_t paddr, unsigned long size) { void __iomem *vaddr; struct apei_iomap *map; unsigned long flags; spin_lock_irqsave(&apei_iomaps_lock, flags); vaddr = __apei_ioremap_fast(paddr, size); spin_unlock_irqrestore(&apei_iomaps_lock, flags); if (vaddr) return vaddr; map = xmalloc(struct apei_iomap); if (!map) return NULL; vaddr = apei_range_map(paddr, size); if (!vaddr) { xfree(map); return NULL; } INIT_LIST_HEAD(&map->list); map->paddr = paddr & PAGE_MASK; map->size = (((paddr + size + PAGE_SIZE -1) & PAGE_MASK) - (paddr & PAGE_MASK)); map->vaddr = vaddr; spin_lock_irqsave(&apei_iomaps_lock, flags); list_add_tail(&map->list, &apei_iomaps); spin_unlock_irqrestore(&apei_iomaps_lock, flags); return map->vaddr + (paddr - map->paddr); } /* * Used to post-unmap the specified IO memory area. */ static void __init apei_post_unmap(paddr_t paddr, unsigned long size) { struct apei_iomap *map; unsigned long flags; spin_lock_irqsave(&apei_iomaps_lock, flags); map = __apei_find_iomap(paddr, size); if (!map) return; list_del(&map->list); spin_unlock_irqrestore(&apei_iomaps_lock, flags); xfree(map); } /* In NMI handler, should set silent = 1 */ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr, int silent) { u32 width, space_id; width = reg->bit_width; space_id = reg->space_id; /* Handle possible alignment issues */ memcpy(paddr, ®->address, sizeof(*paddr)); if (!*paddr) { if (!silent) printk(KERN_WARNING "Invalid physical address in GAR\n"); return -EINVAL; } if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) { if (!silent) printk(KERN_WARNING "Invalid bit width in GAR\n"); return -EINVAL; } if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY && space_id != ACPI_ADR_SPACE_SYSTEM_IO) { if (!silent) printk(KERN_WARNING "Invalid address space type in GAR\n"); return -EINVAL; } return 0; } /* Pre-map, working on GAR */ int __init apei_pre_map_gar(struct acpi_generic_address *reg) { u64 paddr; void __iomem *vaddr; int rc; if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) return 0; rc = apei_check_gar(reg, &paddr, 0); if (rc) return rc; vaddr = apei_pre_map(paddr, reg->bit_width / 8); if (!vaddr) return -EIO; return 0; } /* Post-unmap, working on GAR */ int __init apei_post_unmap_gar(struct acpi_generic_address *reg) { u64 paddr; int rc; if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) return 0; rc = apei_check_gar(reg, &paddr, 0); if (rc) return rc; apei_post_unmap(paddr, reg->bit_width / 8); return 0; } static int apei_read_mem(u64 paddr, u64 *val, u32 width) { void __iomem *addr; u64 tmpval; addr = __apei_ioremap_fast(paddr, width); switch (width) { case 8: *val = readb(addr); break; case 16: *val = readw(addr); break; case 32: *val = readl(addr); break; case 64: tmpval = (u64)readl(addr); tmpval |= ((u64)readl(addr+4)) << 32; *val = tmpval; break; default: return -EINVAL; } return 0; } static int apei_write_mem(u64 paddr, u64 val, u32 width) { void __iomem *addr; u32 tmpval; addr = __apei_ioremap_fast(paddr, width); switch (width) { case 8: writeb(val, addr); break; case 16: writew(val, addr); break; case 32: writel(val, addr); break; case 64: tmpval = (u32)val; writel(tmpval, addr); tmpval = (u32)(val >> 32); writel(tmpval, addr+4); break; default: return -EINVAL; } return 0; } int apei_read(u64 *val, struct acpi_generic_address *reg) { u64 paddr; int rc; rc = apei_check_gar(reg, &paddr, 1); if (rc) return rc; *val = 0; /* currently all erst implementation take bit_width as real range */ switch (reg->space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: return apei_read_mem(paddr, val, reg->bit_width); case ACPI_ADR_SPACE_SYSTEM_IO: return acpi_os_read_port(paddr, (u32 *)val, reg->bit_width); default: return -EINVAL; } } int apei_write(u64 val, struct acpi_generic_address *reg) { u64 paddr; int rc; rc = apei_check_gar(reg, &paddr, 1); if (rc) return rc; switch (reg->space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: return apei_write_mem(paddr, val, reg->bit_width); case ACPI_ADR_SPACE_SYSTEM_IO: return acpi_os_write_port(paddr, val, reg->bit_width); default: return -EINVAL; } } f='#n119'>119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
import sys
import argparse
import os
import os.path

from netlib import tcp
from netlib.http import user_agents
from . import pathoc, version, language


def args_pathoc(argv, stdout=sys.stdout, stderr=sys.stderr):
    preparser = argparse.ArgumentParser(add_help=False)
    preparser.add_argument(
        "--show-uas", dest="showua", action="store_true", default=False,
        help="Print user agent shortcuts and exit."
    )
    pa = preparser.parse_known_args(argv)[0]
    if pa.showua:
        print >> stdout, "User agent strings:"
        for i in user_agents.UASTRINGS:
            print >> stdout, "  ", i[1], i[0]
        sys.exit(0)

    parser = argparse.ArgumentParser(
        description='A perverse HTTP client.', parents=[preparser]
    )
    parser.add_argument(
        '--version',
        action='version',
        version="pathoc " + version.VERSION
    )
    parser.add_argument(
        "-c", dest="connect_to", type=str, default=False,
        metavar="HOST:PORT",
        help="Issue an HTTP CONNECT to connect to the specified host."
    )
    parser.add_argument(
        "--memo-limit", dest='memolimit', default=5000, type=int, metavar="N",
        help='Stop if we do not find a valid request after N attempts.'
    )
    parser.add_argument(
        "-m", dest='memo', action="store_true", default=False,
        help="""
            Remember specs, and never play the same one twice. Note that this
            means requests have to be rendered in memory, which means that
            large generated data can cause issues.
        """
    )
    parser.add_argument(
        "-n", dest='repeat', default=1, type=int, metavar="N",
        help='Repeat N times. If 0 repeat for ever.'
    )
    parser.add_argument(
        "-w", dest='wait', default=0, type=float, metavar="N",
        help='Wait N seconds between each request.'
    )
    parser.add_argument(
        "-r", dest="random", action="store_true", default=False,
        help="""
        Select a random request from those specified. If this is not specified,
        requests are all played in sequence.
        """
    )
    parser.add_argument(
        "-t", dest="timeout", type=int, default=None,
        help="Connection timeout"
    )
    parser.add_argument(
        "--http2", dest="use_http2", action="store_true", default=False,
        help='Perform all requests over a single HTTP/2 connection.'
    )
    parser.add_argument(
        "--http2-skip-connection-preface",
        dest="http2_skip_connection_preface",
        action="store_true",
        default=False,
        help='Skips the HTTP/2 connection preface before sending requests.')

    parser.add_argument(
        'host', type=str,
        metavar="host[:port]",
        help='Host and port to connect to'
    )
    parser.add_argument(
        'requests', type=str, nargs="+",
        help="""
        Request specification, or path to a file containing request
        specifcations
        """
    )

    group = parser.add_argument_group(
        'SSL',
    )
    group.add_argument(
        "-s", dest="ssl", action="store_true", default=False,
        help="Connect with SSL"
    )
    group.add_argument(
        "-C", dest="clientcert", type=str, default=False,
        help="Path to a file containing client certificate and private key"
    )
    group.add_argument(
        "-i", dest="sni", type=str, default=False,
        help="SSL Server Name Indication"
    )
    group.add_argument(
        "--ciphers", dest="ciphers", type=str, default=False,
        help="SSL cipher specification"
    )
    group.add_argument(
        "--ssl-version", dest="ssl_version", type=str, default="secure",
        choices=tcp.sslversion_choices.keys(),
        help="Set supported SSL/TLS versions. "
             "SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+."
    )

    group = parser.add_argument_group(
        'Controlling Output',
        """
            Some of these options expand generated values for logging - if
            you're generating large data, use them with caution.
        """
    )
    group.add_argument(
        "-I", dest="ignorecodes", type=str, default="",
        help="Comma-separated list of response codes to ignore"
    )
    group.add_argument(
        "-S", dest="showssl", action="store_true", default=False,
        help="Show info on SSL connection"
    )
    group.add_argument(
        "-e", dest="explain", action="store_true", default=False,
        help="Explain requests"
    )
    group.add_argument(
        "-o", dest="oneshot", action="store_true", default=False,
        help="Oneshot - exit after first non-ignored response"
    )
    group.add_argument(
        "-q", dest="showreq", action="store_true", default=False,
        help="Print full request"
    )
    group.add_argument(
        "-p", dest="showresp", action="store_true", default=False,
        help="Print full response"
    )
    group.add_argument(
        "-T", dest="ignoretimeout", action="store_true", default=False,
        help="Ignore timeouts"
    )
    group.add_argument(
        "-x", dest="hexdump", action="store_true", default=False,
        help="Output in hexdump format"
    )
    group.add_argument(
        "--http2-framedump", dest="http2_framedump", action="store_true", default=False,
        help="Output all received & sent HTTP/2 frames"
    )

    args = parser.parse_args(argv[1:])

    args.ssl_version, args.ssl_options = tcp.sslversion_choices[args.ssl_version]

    args.port = None
    if ":" in args.host:
        h, p = args.host.rsplit(":", 1)
        try:
            p = int(p)
        except ValueError:
            return parser.error("Invalid port in host spec: %s" % args.host)
        args.host = h
        args.port = p

    if args.port is None:
        args.port = 443 if args.ssl else 80

    try:
        args.ignorecodes = [int(i) for i in args.ignorecodes.split(",") if i]
    except ValueError:
        return parser.error(
            "Invalid return code specification: %s" %
            args.ignorecodes)

    if args.connect_to:
        parts = args.connect_to.split(":")
        if len(parts) != 2:
            return parser.error(
                "Invalid CONNECT specification: %s" %
                args.connect_to)
        try:
            parts[1] = int(parts[1])
        except ValueError:
            return parser.error(
                "Invalid CONNECT specification: %s" %
                args.connect_to)
        args.connect_to = parts
    else:
        args.connect_to = None

    if args.http2_skip_connection_preface:
        args.use_http2 = True

    if args.use_http2:
        args.ssl = True

    reqs = []
    for r in args.requests:
        if os.path.isfile(r):
            data = open(r).read()
            r = data
        try:
            reqs.append(language.parse_pathoc(r, args.use_http2))
        except language.ParseException as v:
            print >> stderr, "Error parsing request spec: %s" % v.msg
            print >> stderr, v.marked()
            sys.exit(1)
    args.requests = reqs

    return args


def go_pathoc():  # pragma: nocover
    args = args_pathoc(sys.argv)
    pathoc.main(args)