Merge branch 'master' into upstream

This commit is contained in:
Jeff Garzik 2007-02-17 15:11:43 -05:00
commit f630fe2817
1487 changed files with 43559 additions and 15646 deletions

96
.mailmap Normal file
View File

@ -0,0 +1,96 @@
#
# This list is used by git-shortlog to fix a few botched name translations
# in the git archive, either because the author's full name was messed up
# and/or not always written the same way, making contributions from the
# same person appearing not to be so or badly displayed.
#
# repo-abbrev: /pub/scm/linux/kernel/git/
#
Aaron Durbin <adurbin@google.com>
Adam Oldham <oldhamca@gmail.com>
Adam Radford <aradford@gmail.com>
Adrian Bunk <bunk@stusta.de>
Alan Cox <alan@lxorguk.ukuu.org.uk>
Alan Cox <root@hraefn.swansea.linux.org.uk>
Aleksey Gorelov <aleksey_gorelov@phoenix.com>
Al Viro <viro@ftp.linux.org.uk>
Al Viro <viro@zenIV.linux.org.uk>
Andreas Herrmann <aherrman@de.ibm.com>
Andrew Morton <akpm@osdl.org>
Andrew Vasquez <andrew.vasquez@qlogic.com>
Andy Adamson <andros@citi.umich.edu>
Arnaud Patard <arnaud.patard@rtp-net.org>
Arnd Bergmann <arnd@arndb.de>
Axel Dyks <xl@xlsigned.net>
Ben Gardner <bgardner@wabtec.com>
Ben M Cahill <ben.m.cahill@intel.com>
Björn Steinbrink <B.Steinbrink@gmx.de>
Brian Avery <b.avery@hp.com>
Brian King <brking@us.ibm.com>
Christoph Hellwig <hch@lst.de>
Corey Minyard <minyard@acm.org>
David Brownell <david-b@pacbell.net>
David Woodhouse <dwmw2@shinybook.infradead.org>
Domen Puncer <domen@coderock.org>
Douglas Gilbert <dougg@torque.net>
Ed L. Cashin <ecashin@coraid.com>
Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Felipe W Damasio <felipewd@terra.com.br>
Felix Kuhling <fxkuehl@gmx.de>
Felix Moeller <felix@derklecks.de>
Filipe Lautert <filipe@icewall.org>
Franck Bui-Huu <vagabon.xyz@gmail.com>
Frank Zago <fzago@systemfabricworks.com>
Greg Kroah-Hartman <greg@echidna.(none)>
Greg Kroah-Hartman <gregkh@suse.de>
Greg Kroah-Hartman <greg@kroah.com>
Henk Vergonet <Henk.Vergonet@gmail.com>
Henrik Kretzschmar <henne@nachtwindheim.de>
Herbert Xu <herbert@gondor.apana.org.au>
Jacob Shin <Jacob.Shin@amd.com>
James Bottomley <jejb@mulgrave.(none)>
James Bottomley <jejb@titanic.il.steeleye.com>
James E Wilson <wilson@specifix.com>
James Ketrenos <jketreno@io.(none)>
Jean Tourrilhes <jt@hpl.hp.com>
Jeff Garzik <jgarzik@pretzel.yyz.us>
Jens Axboe <axboe@suse.de>
Jens Osterkamp <Jens.Osterkamp@de.ibm.com>
John Stultz <johnstul@us.ibm.com>
Juha Yrjola <at solidboot.com>
Juha Yrjola <juha.yrjola@nokia.com>
Juha Yrjola <juha.yrjola@solidboot.com>
Kay Sievers <kay.sievers@vrfy.org>
Kenneth W Chen <kenneth.w.chen@intel.com>
Koushik <raghavendra.koushik@neterion.com>
Leonid I Ananiev <leonid.i.ananiev@intel.com>
Linas Vepstas <linas@austin.ibm.com>
Matthieu CASTET <castet.matthieu@free.fr>
Michel Dänzer <michel@tungstengraphics.com>
Mitesh shah <mshah@teja.com>
Morten Welinder <terra@gnome.org>
Morten Welinder <welinder@anemone.rentec.com>
Morten Welinder <welinder@darter.rentec.com>
Morten Welinder <welinder@troll.com>
Nguyen Anh Quynh <aquynh@gmail.com>
Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Patrick Mochel <mochel@digitalimplant.org>
Peter A Jonsson <pj@ludd.ltu.se>
Praveen BP <praveenbp@ti.com>
Rajesh Shah <rajesh.shah@intel.com>
Ralf Baechle <ralf@linux-mips.org>
Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Rémi Denis-Courmont <rdenis@simphalempin.com>
Rudolf Marek <R.Marek@sh.cvut.cz>
Rui Saraiva <rmps@joel.ist.utl.pt>
Sachin P Sant <ssant@in.ibm.com>
Sam Ravnborg <sam@mars.ravnborg.org>
Simon Kelley <simon@thekelleys.org.uk>
Stéphane Witzmann <stephane.witzmann@ubpmes.univ-bpclermont.fr>
Stephen Hemminger <shemminger@osdl.org>
Tejun Heo <htejun@gmail.com>
Thomas Graf <tgraf@suug.ch>
Tony Luck <tony.luck@intel.com>
Tsuneo Yoshioka <Tsuneo.Yoshioka@f-secure.com>
Valdis Kletnieks <Valdis.Kletnieks@vt.edu>

View File

@ -78,7 +78,8 @@ Identifying GPIOs
-----------------
GPIOs are identified by unsigned integers in the range 0..MAX_INT. That
reserves "negative" numbers for other purposes like marking signals as
"not available on this board", or indicating faults.
"not available on this board", or indicating faults. Code that doesn't
touch the underlying hardware treats these integers as opaque cookies.
Platforms define how they use those integers, and usually #define symbols
for the GPIO lines so that board-specific setup code directly corresponds
@ -139,10 +140,10 @@ issues including wire-OR and output latencies.
The get/set calls have no error returns because "invalid GPIO" should have
been reported earlier in gpio_set_direction(). However, note that not all
platforms can read the value of output pins; those that can't should always
return zero. Also, these calls will be ignored for GPIOs that can't safely
be accessed wihtout sleeping (see below).
return zero. Also, using these calls for GPIOs that can't safely be accessed
without sleeping (see below) is an error.
Platform-specific implementations are encouraged to optimise the two
Platform-specific implementations are encouraged to optimize the two
calls to access the GPIO value in cases where the GPIO number (and for
output, value) are constant. It's normal for them to need only a couple
of instructions in such cases (reading or writing a hardware register),
@ -239,7 +240,8 @@ options are part of the IRQ interface, e.g. IRQF_TRIGGER_FALLING, as are
system wakeup capabilities.
Non-error values returned from irq_to_gpio() would most commonly be used
with gpio_get_value().
with gpio_get_value(), for example to initialize or update driver state
when the IRQ is edge-triggered.
@ -260,9 +262,10 @@ pullups (or pulldowns) so that the on-chip ones should not be used.
There are other system-specific mechanisms that are not specified here,
like the aforementioned options for input de-glitching and wire-OR output.
Hardware may support reading or writing GPIOs in gangs, but that's usually
configuration dependednt: for GPIOs sharing the same bank. (GPIOs are
configuration dependent: for GPIOs sharing the same bank. (GPIOs are
commonly grouped in banks of 16 or 32, with a given SOC having several such
banks.) Code relying on such mechanisms will necessarily be nonportable.
banks.) Some systems can trigger IRQs from output GPIOs. Code relying on
such mechanisms will necessarily be nonportable.
Dynamic definition of GPIOs is not currently supported; for example, as
a side effect of configuring an add-on board with some GPIO expanders.

View File

@ -0,0 +1,68 @@
timer_stats - timer usage statistics
------------------------------------
timer_stats is a debugging facility to make the timer (ab)usage in a Linux
system visible to kernel and userspace developers. It is not intended for
production usage as it adds significant overhead to the (hr)timer code and the
(hr)timer data structures.
timer_stats should be used by kernel and userspace developers to verify that
their code does not make unduly use of timers. This helps to avoid unnecessary
wakeups, which should be avoided to optimize power consumption.
It can be enabled by CONFIG_TIMER_STATS in the "Kernel hacking" configuration
section.
timer_stats collects information about the timer events which are fired in a
Linux system over a sample period:
- the pid of the task(process) which initialized the timer
- the name of the process which initialized the timer
- the function where the timer was intialized
- the callback function which is associated to the timer
- the number of events (callbacks)
timer_stats adds an entry to /proc: /proc/timer_stats
This entry is used to control the statistics functionality and to read out the
sampled information.
The timer_stats functionality is inactive on bootup.
To activate a sample period issue:
# echo 1 >/proc/timer_stats
To stop a sample period issue:
# echo 0 >/proc/timer_stats
The statistics can be retrieved by:
# cat /proc/timer_stats
The readout of /proc/timer_stats automatically disables sampling. The sampled
information is kept until a new sample period is started. This allows multiple
readouts.
Sample output of /proc/timer_stats:
Timerstats sample period: 3.888770 s
12, 0 swapper hrtimer_stop_sched_tick (hrtimer_sched_tick)
15, 1 swapper hcd_submit_urb (rh_timer_func)
4, 959 kedac schedule_timeout (process_timeout)
1, 0 swapper page_writeback_init (wb_timer_fn)
28, 0 swapper hrtimer_stop_sched_tick (hrtimer_sched_tick)
22, 2948 IRQ 4 tty_flip_buffer_push (delayed_work_timer_fn)
3, 3100 bash schedule_timeout (process_timeout)
1, 1 swapper queue_delayed_work_on (delayed_work_timer_fn)
1, 1 swapper queue_delayed_work_on (delayed_work_timer_fn)
1, 1 swapper neigh_table_init_no_netlink (neigh_periodic_timer)
1, 2292 ip __netdev_watchdog_up (dev_watchdog)
1, 23 events/1 do_cache_clean (delayed_work_timer_fn)
90 total events, 30.0 events/sec
The first column is the number of events, the second column the pid, the third
column is the name of the process. The forth column shows the function which
initialized the timer and in parantheses the callback function which was
executed on expiry.
Thomas, Ingo

View File

@ -0,0 +1,249 @@
High resolution timers and dynamic ticks design notes
-----------------------------------------------------
Further information can be found in the paper of the OLS 2006 talk "hrtimers
and beyond". The paper is part of the OLS 2006 Proceedings Volume 1, which can
be found on the OLS website:
http://www.linuxsymposium.org/2006/linuxsymposium_procv1.pdf
The slides to this talk are available from:
http://tglx.de/projects/hrtimers/ols2006-hrtimers.pdf
The slides contain five figures (pages 2, 15, 18, 20, 22), which illustrate the
changes in the time(r) related Linux subsystems. Figure #1 (p. 2) shows the
design of the Linux time(r) system before hrtimers and other building blocks
got merged into mainline.
Note: the paper and the slides are talking about "clock event source", while we
switched to the name "clock event devices" in meantime.
The design contains the following basic building blocks:
- hrtimer base infrastructure
- timeofday and clock source management
- clock event management
- high resolution timer functionality
- dynamic ticks
hrtimer base infrastructure
---------------------------
The hrtimer base infrastructure was merged into the 2.6.16 kernel. Details of
the base implementation are covered in Documentation/hrtimers/hrtimer.txt. See
also figure #2 (OLS slides p. 15)
The main differences to the timer wheel, which holds the armed timer_list type
timers are:
- time ordered enqueueing into a rb-tree
- independent of ticks (the processing is based on nanoseconds)
timeofday and clock source management
-------------------------------------
John Stultz's Generic Time Of Day (GTOD) framework moves a large portion of
code out of the architecture-specific areas into a generic management
framework, as illustrated in figure #3 (OLS slides p. 18). The architecture
specific portion is reduced to the low level hardware details of the clock
sources, which are registered in the framework and selected on a quality based
decision. The low level code provides hardware setup and readout routines and
initializes data structures, which are used by the generic time keeping code to
convert the clock ticks to nanosecond based time values. All other time keeping
related functionality is moved into the generic code. The GTOD base patch got
merged into the 2.6.18 kernel.
Further information about the Generic Time Of Day framework is available in the
OLS 2005 Proceedings Volume 1:
http://www.linuxsymposium.org/2005/linuxsymposium_procv1.pdf
The paper "We Are Not Getting Any Younger: A New Approach to Time and
Timers" was written by J. Stultz, D.V. Hart, & N. Aravamudan.
Figure #3 (OLS slides p.18) illustrates the transformation.
clock event management
----------------------
While clock sources provide read access to the monotonically increasing time
value, clock event devices are used to schedule the next event
interrupt(s). The next event is currently defined to be periodic, with its
period defined at compile time. The setup and selection of the event device
for various event driven functionalities is hardwired into the architecture
dependent code. This results in duplicated code across all architectures and
makes it extremely difficult to change the configuration of the system to use
event interrupt devices other than those already built into the
architecture. Another implication of the current design is that it is necessary
to touch all the architecture-specific implementations in order to provide new
functionality like high resolution timers or dynamic ticks.
The clock events subsystem tries to address this problem by providing a generic
solution to manage clock event devices and their usage for the various clock
event driven kernel functionalities. The goal of the clock event subsystem is
to minimize the clock event related architecture dependent code to the pure
hardware related handling and to allow easy addition and utilization of new
clock event devices. It also minimizes the duplicated code across the
architectures as it provides generic functionality down to the interrupt
service handler, which is almost inherently hardware dependent.
Clock event devices are registered either by the architecture dependent boot
code or at module insertion time. Each clock event device fills a data
structure with clock-specific property parameters and callback functions. The
clock event management decides, by using the specified property parameters, the
set of system functions a clock event device will be used to support. This
includes the distinction of per-CPU and per-system global event devices.
System-level global event devices are used for the Linux periodic tick. Per-CPU
event devices are used to provide local CPU functionality such as process
accounting, profiling, and high resolution timers.
The management layer assignes one or more of the folliwing functions to a clock
event device:
- system global periodic tick (jiffies update)
- cpu local update_process_times
- cpu local profiling
- cpu local next event interrupt (non periodic mode)
The clock event device delegates the selection of those timer interrupt related
functions completely to the management layer. The clock management layer stores
a function pointer in the device description structure, which has to be called
from the hardware level handler. This removes a lot of duplicated code from the
architecture specific timer interrupt handlers and hands the control over the
clock event devices and the assignment of timer interrupt related functionality
to the core code.
The clock event layer API is rather small. Aside from the clock event device
registration interface it provides functions to schedule the next event
interrupt, clock event device notification service and support for suspend and
resume.
The framework adds about 700 lines of code which results in a 2KB increase of
the kernel binary size. The conversion of i386 removes about 100 lines of
code. The binary size decrease is in the range of 400 byte. We believe that the
increase of flexibility and the avoidance of duplicated code across
architectures justifies the slight increase of the binary size.
The conversion of an architecture has no functional impact, but allows to
utilize the high resolution and dynamic tick functionalites without any change
to the clock event device and timer interrupt code. After the conversion the
enabling of high resolution timers and dynamic ticks is simply provided by
adding the kernel/time/Kconfig file to the architecture specific Kconfig and
adding the dynamic tick specific calls to the idle routine (a total of 3 lines
added to the idle function and the Kconfig file)
Figure #4 (OLS slides p.20) illustrates the transformation.
high resolution timer functionality
-----------------------------------
During system boot it is not possible to use the high resolution timer
functionality, while making it possible would be difficult and would serve no
useful function. The initialization of the clock event device framework, the
clock source framework (GTOD) and hrtimers itself has to be done and
appropriate clock sources and clock event devices have to be registered before
the high resolution functionality can work. Up to the point where hrtimers are
initialized, the system works in the usual low resolution periodic mode. The
clock source and the clock event device layers provide notification functions
which inform hrtimers about availability of new hardware. hrtimers validates
the usability of the registered clock sources and clock event devices before
switching to high resolution mode. This ensures also that a kernel which is
configured for high resolution timers can run on a system which lacks the
necessary hardware support.
The high resolution timer code does not support SMP machines which have only
global clock event devices. The support of such hardware would involve IPI
calls when an interrupt happens. The overhead would be much larger than the
benefit. This is the reason why we currently disable high resolution and
dynamic ticks on i386 SMP systems which stop the local APIC in C3 power
state. A workaround is available as an idea, but the problem has not been
tackled yet.
The time ordered insertion of timers provides all the infrastructure to decide
whether the event device has to be reprogrammed when a timer is added. The
decision is made per timer base and synchronized across per-cpu timer bases in
a support function. The design allows the system to utilize separate per-CPU
clock event devices for the per-CPU timer bases, but currently only one
reprogrammable clock event device per-CPU is utilized.
When the timer interrupt happens, the next event interrupt handler is called
from the clock event distribution code and moves expired timers from the
red-black tree to a separate double linked list and invokes the softirq
handler. An additional mode field in the hrtimer structure allows the system to
execute callback functions directly from the next event interrupt handler. This
is restricted to code which can safely be executed in the hard interrupt
context. This applies, for example, to the common case of a wakeup function as
used by nanosleep. The advantage of executing the handler in the interrupt
context is the avoidance of up to two context switches - from the interrupted
context to the softirq and to the task which is woken up by the expired
timer.
Once a system has switched to high resolution mode, the periodic tick is
switched off. This disables the per system global periodic clock event device -
e.g. the PIT on i386 SMP systems.
The periodic tick functionality is provided by an per-cpu hrtimer. The callback
function is executed in the next event interrupt context and updates jiffies
and calls update_process_times and profiling. The implementation of the hrtimer
based periodic tick is designed to be extended with dynamic tick functionality.
This allows to use a single clock event device to schedule high resolution
timer and periodic events (jiffies tick, profiling, process accounting) on UP
systems. This has been proved to work with the PIT on i386 and the Incrementer
on PPC.
The softirq for running the hrtimer queues and executing the callbacks has been
separated from the tick bound timer softirq to allow accurate delivery of high
resolution timer signals which are used by itimer and POSIX interval
timers. The execution of this softirq can still be delayed by other softirqs,
but the overall latencies have been significantly improved by this separation.
Figure #5 (OLS slides p.22) illustrates the transformation.
dynamic ticks
-------------
Dynamic ticks are the logical consequence of the hrtimer based periodic tick
replacement (sched_tick). The functionality of the sched_tick hrtimer is
extended by three functions:
- hrtimer_stop_sched_tick
- hrtimer_restart_sched_tick
- hrtimer_update_jiffies
hrtimer_stop_sched_tick() is called when a CPU goes into idle state. The code
evaluates the next scheduled timer event (from both hrtimers and the timer
wheel) and in case that the next event is further away than the next tick it
reprograms the sched_tick to this future event, to allow longer idle sleeps
without worthless interruption by the periodic tick. The function is also
called when an interrupt happens during the idle period, which does not cause a
reschedule. The call is necessary as the interrupt handler might have armed a
new timer whose expiry time is before the time which was identified as the
nearest event in the previous call to hrtimer_stop_sched_tick.
hrtimer_restart_sched_tick() is called when the CPU leaves the idle state before
it calls schedule(). hrtimer_restart_sched_tick() resumes the periodic tick,
which is kept active until the next call to hrtimer_stop_sched_tick().
hrtimer_update_jiffies() is called from irq_enter() when an interrupt happens
in the idle period to make sure that jiffies are up to date and the interrupt
handler has not to deal with an eventually stale jiffy value.
The dynamic tick feature provides statistical values which are exported to
userspace via /proc/stats and can be made available for enhanced power
management control.
The implementation leaves room for further development like full tickless
systems, where the time slice is controlled by the scheduler, variable
frequency profiling, and a complete removal of jiffies in the future.
Aside the current initial submission of i386 support, the patchset has been
extended to x86_64 and ARM already. Initial (work in progress) support is also
available for MIPS and PowerPC.
Thomas, Ingo

View File

@ -48,14 +48,9 @@ following:
The SMBus controller is function 3 in device 1f. Class 0c05 is SMBus Serial
Controller.
If you do NOT see the 24x3 device at function 3, and you can't figure out
any way in the BIOS to enable it,
The ICH chips are quite similar to Intel's PIIX4 chip, at least in the
SMBus controller.
See the file i2c-piix4 for some additional information.
Process Call Support
--------------------
@ -74,6 +69,61 @@ SMBus 2.0 Support
The 82801DB (ICH4) and later chips support several SMBus 2.0 features.
Hidden ICH SMBus
----------------
If your system has an Intel ICH south bridge, but you do NOT see the
SMBus device at 00:1f.3 in lspci, and you can't figure out any way in the
BIOS to enable it, it means it has been hidden by the BIOS code. Asus is
well known for first doing this on their P4B motherboard, and many other
boards after that. Some vendor machines are affected as well.
The first thing to try is the "i2c_ec" ACPI driver. It could be that the
SMBus was hidden on purpose because it'll be driven by ACPI. If the
i2c_ec driver works for you, just forget about the i2c-i801 driver and
don't try to unhide the ICH SMBus. Even if i2c_ec doesn't work, you
better make sure that the SMBus isn't used by the ACPI code. Try loading
the "fan" and "thermal" drivers, and check in /proc/acpi/fan and
/proc/acpi/thermal_zone. If you find anything there, it's likely that
the ACPI is accessing the SMBus and it's safer not to unhide it. Only
once you are certain that ACPI isn't using the SMBus, you can attempt
to unhide it.
In order to unhide the SMBus, we need to change the value of a PCI
register before the kernel enumerates the PCI devices. This is done in
drivers/pci/quirks.c, where all affected boards must be listed (see
function asus_hides_smbus_hostbridge.) If the SMBus device is missing,
and you think there's something interesting on the SMBus (e.g. a
hardware monitoring chip), you need to add your board to the list.
The motherboard is identified using the subvendor and subdevice IDs of the
host bridge PCI device. Get yours with "lspci -n -v -s 00:00.0":
00:00.0 Class 0600: 8086:2570 (rev 02)
Subsystem: 1043:80f2
Flags: bus master, fast devsel, latency 0
Memory at fc000000 (32-bit, prefetchable) [size=32M]
Capabilities: [e4] #09 [2106]
Capabilities: [a0] AGP version 3.0
Here the host bridge ID is 2570 (82865G/PE/P), the subvendor ID is 1043
(Asus) and the subdevice ID is 80f2 (P4P800-X). You can find the symbolic
names for the bridge ID and the subvendor ID in include/linux/pci_ids.h,
and then add a case for your subdevice ID at the right place in
drivers/pci/quirks.c. Then please give it very good testing, to make sure
that the unhidden SMBus doesn't conflict with e.g. ACPI.
If it works, proves useful (i.e. there are usable chips on the SMBus)
and seems safe, please submit a patch for inclusion into the kernel.
Note: There's a useful script in lm_sensors 2.10.2 and later, named
unhide_ICH_SMBus (in prog/hotplug), which uses the fakephp driver to
temporarily unhide the SMBus without having to patch and recompile your
kernel. It's very convenient if you just want to check if there's
anything interesting on your hidden ICH SMBus.
**********************
The lm_sensors project gratefully acknowledges the support of Texas
Instruments in the initial development of this driver.

View File

@ -19,6 +19,7 @@ It currently supports the following devices:
* (type=4) Analog Devices ADM1032 evaluation board
* (type=5) Analog Devices evaluation boards: ADM1025, ADM1030, ADM1031
* (type=6) Barco LPT->DVI (K5800236) adapter
* (type=7) One For All JP1 parallel port adapter
These devices use different pinout configurations, so you have to tell
the driver what you have, using the type module parameter. There is no
@ -157,3 +158,17 @@ many more, using /dev/velleman.
http://home.wanadoo.nl/hihihi/libk8005.htm
http://struyve.mine.nu:8080/index.php?block=k8000
http://sourceforge.net/projects/libk8005/
One For All JP1 parallel port adapter
-------------------------------------
The JP1 project revolves around a set of remote controls which expose
the I2C bus their internal configuration EEPROM lives on via a 6 pin
jumper in the battery compartment. More details can be found at:
http://www.hifi-remote.com/jp1/
Details of the simple parallel port hardware can be found at:
http://www.hifi-remote.com/jp1/hardware.shtml

View File

@ -6,7 +6,7 @@ Supported adapters:
Datasheet: Publicly available at the Intel website
* ServerWorks OSB4, CSB5, CSB6 and HT-1000 southbridges
Datasheet: Only available via NDA from ServerWorks
* ATI IXP southbridges IXP200, IXP300, IXP400
* ATI IXP200, IXP300, IXP400 and SB600 southbridges
Datasheet: Not publicly available
* Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
Datasheet: Publicly available at the SMSC website http://www.smsc.com

View File

@ -13,6 +13,9 @@ Supported adapters:
* VIA Technologies, Inc. VT8235, VT8237R, VT8237A, VT8251
Datasheet: available on request and under NDA from VIA
* VIA Technologies, Inc. CX700
Datasheet: available on request and under NDA from VIA
Authors:
Kyösti Mälkki <kmalkki@cc.hut.fi>,
Mark D. Studebaker <mdsxyz123@yahoo.com>,
@ -44,6 +47,7 @@ Your lspci -n listing must show one of these :
device 1106:3227 (VT8237R)
device 1106:3337 (VT8237A)
device 1106:3287 (VT8251)
device 1106:8324 (CX700)
If none of these show up, you should look in the BIOS for settings like
enable ACPI / SMBus or even USB.
@ -51,3 +55,6 @@ enable ACPI / SMBus or even USB.
Except for the oldest chips (VT82C596A/B, VT82C686A and most probably
VT8231), this driver supports I2C block transactions. Such transactions
are mainly useful to read from and write to EEPROMs.
The CX700 additionally appears to support SMBus PEC, although this driver
doesn't implement it yet.

View File

@ -129,6 +129,12 @@ Technical changes:
structure, those name member should be initialized to a driver name
string. i2c_driver itself has no name member anymore.
* [Driver model] Instead of shutdown or reboot notifiers, provide a
shutdown() method in your driver.
* [Power management] Use the driver model suspend() and resume()
callbacks instead of the obsolete pm_register() calls.
Coding policy:
* [Copyright] Use (C), not (c), for copyright.

View File

@ -97,7 +97,7 @@ SMBus Write Word Data
=====================
This is the opposite operation of the Read Word Data command. 16 bits
of data is read from a device, from a designated register that is
of data is written to a device, to the designated register that is
specified through the Comm byte.
S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P

View File

@ -21,20 +21,26 @@ The driver structure
Usually, you will implement a single driver structure, and instantiate
all clients from it. Remember, a driver structure contains general access
routines, a client structure specific information like the actual I2C
address.
routines, and should be zero-initialized except for fields with data you
provide. A client structure holds device-specific information like the
driver model device node, and its I2C address.
static struct i2c_driver foo_driver = {
.driver = {
.name = "foo",
},
.attach_adapter = &foo_attach_adapter,
.detach_client = &foo_detach_client,
.command = &foo_command /* may be NULL */
.attach_adapter = foo_attach_adapter,
.detach_client = foo_detach_client,
.shutdown = foo_shutdown, /* optional */
.suspend = foo_suspend, /* optional */
.resume = foo_resume, /* optional */
.command = foo_command, /* optional */
}
The name field must match the driver name, including the case. It must not
contain spaces, and may be up to 31 characters long.
The name field is the driver name, and must not contain spaces. It
should match the module name (if the driver can be compiled as a module),
although you can use MODULE_ALIAS (passing "foo" in this example) to add
another name for the module.
All other fields are for call-back functions which will be explained
below.
@ -43,11 +49,18 @@ below.
Extra client data
=================
The client structure has a special `data' field that can point to any
structure at all. You can use this to keep client-specific data. You
Each client structure has a special `data' field that can point to any
structure at all. You should use this to keep device-specific data,
especially in drivers that handle multiple I2C or SMBUS devices. You
do not always need this, but especially for `sensors' drivers, it can
be very useful.
/* store the value */
void i2c_set_clientdata(struct i2c_client *client, void *data);
/* retrieve the value */
void *i2c_get_clientdata(struct i2c_client *client);
An example structure is below.
struct foo_data {
@ -493,6 +506,33 @@ by `__init_data'. Hose functions and structures can be removed after
kernel booting (or module loading) is completed.
Power Management
================
If your I2C device needs special handling when entering a system low
power state -- like putting a transceiver into a low power mode, or
activating a system wakeup mechanism -- do that in the suspend() method.
The resume() method should reverse what the suspend() method does.
These are standard driver model calls, and they work just like they
would for any other driver stack. The calls can sleep, and can use
I2C messaging to the device being suspended or resumed (since their
parent I2C adapter is active when these calls are issued, and IRQs
are still enabled).
System Shutdown
===============
If your I2C device needs special handling when the system shuts down
or reboots (including kexec) -- like turning something off -- use a
shutdown() method.
Again, this is a standard driver model call, working just like it
would for any other driver stack: the calls can sleep, and can use
I2C messaging.
Command function
================

View File

@ -104,6 +104,9 @@ loader, and have no meaning to the kernel directly.
Do not modify the syntax of boot loader parameters without extreme
need or coordination with <Documentation/i386/boot.txt>.
There are also arch-specific kernel-parameters not documented here.
See for example <Documentation/x86_64/boot-options.txt>.
Note that ALL kernel parameters listed below are CASE SENSITIVE, and that
a trailing = on the name of any parameter states that that parameter will
be entered as an environment variable, whereas its absence indicates that
@ -361,6 +364,11 @@ and is between 256 and 4096 characters. It is defined in the file
clocksource is not available, it defaults to PIT.
Format: { pit | tsc | cyclone | pmtmr }
code_bytes [IA32] How many bytes of object code to print in an
oops report.
Range: 0 - 8192
Default: 64
disable_8254_timer
enable_8254_timer
[IA32/X86_64] Disable/Enable interrupt 0 timer routing
@ -601,6 +609,10 @@ and is between 256 and 4096 characters. It is defined in the file
highmem otherwise. This also works to reduce highmem
size on bigger boxes.
highres= [KNL] Enable/disable high resolution timer mode.
Valid parameters: "on", "off"
Default: "on"
hisax= [HW,ISDN]
See Documentation/isdn/README.HiSax.
@ -1070,6 +1082,10 @@ and is between 256 and 4096 characters. It is defined in the file
in certain environments such as networked servers or
real-time systems.
nohz= [KNL] Boottime enable/disable dynamic ticks
Valid arguments: on, off
Default: on
noirqbalance [IA-32,SMP,KNL] Disable kernel irq balancing
noirqdebug [IA-32] Disables the code which attempts to detect and

View File

@ -1334,6 +1334,9 @@ platforms are moved over to use the flattened-device-tree model.
fsl-usb2-mph compatible controllers. Either this property or
"port0" (or both) must be defined for "fsl-usb2-mph" compatible
controllers.
- dr_mode : indicates the working mode for "fsl-usb2-dr" compatible
controllers. Can be "host", "peripheral", or "otg". Default to
"host" if not defined for backward compatibility.
Recommended properties :
- interrupts : <a b> where a is the interrupt number and b is a
@ -1367,6 +1370,7 @@ platforms are moved over to use the flattened-device-tree model.
#size-cells = <0>;
interrupt-parent = <700>;
interrupts = <26 1>;
dr_mode = "otg";
phy = "ulpi";
};

View File

@ -1,7 +1,7 @@
MPC52xx Device Tree Bindings
MPC5200 Device Tree Bindings
----------------------------
(c) 2006 Secret Lab Technologies Ltd
(c) 2006-2007 Secret Lab Technologies Ltd
Grant Likely <grant.likely at secretlab.ca>
********** DRAFT ***********
@ -20,11 +20,11 @@ described in Documentation/powerpc/booting-without-of.txt), or passed
by Open Firmare (IEEE 1275) compatible firmware using an OF compatible
client interface API.
This document specifies the requirements on the device-tree for mpc52xx
This document specifies the requirements on the device-tree for mpc5200
based boards. These requirements are above and beyond the details
specified in either the OpenFirmware spec or booting-without-of.txt
All new mpc52xx-based boards are expected to match this document. In
All new mpc5200-based boards are expected to match this document. In
cases where this document is not sufficient to support a new board port,
this document should be updated as part of adding the new board support.
@ -32,26 +32,26 @@ II - Philosophy
===============
The core of this document is naming convention. The whole point of
defining this convention is to reduce or eliminate the number of
special cases required to support a 52xx board. If all 52xx boards
follow the same convention, then generic 52xx support code will work
special cases required to support a 5200 board. If all 5200 boards
follow the same convention, then generic 5200 support code will work
rather than coding special cases for each new board.
This section tries to capture the thought process behind why the naming
convention is what it is.
1. Node names
-------------
1. names
---------
There is strong convention/requirements already established for children
of the root node. 'cpus' describes the processor cores, 'memory'
describes memory, and 'chosen' provides boot configuration. Other nodes
are added to describe devices attached to the processor local bus.
Following convention already established with other system-on-chip
processors, MPC52xx boards must have an 'soc5200' node as a child of the
root node.
The soc5200 node holds child nodes for all on chip devices. Child nodes
are typically named after the configured function. ie. the FEC node is
named 'ethernet', and a PSC in uart mode is named 'serial'.
Following convention already established with other system-on-chip
processors, 5200 device trees should use the name 'soc5200' for the
parent node of on chip devices, and the root node should be its parent.
Child nodes are typically named after the configured function. ie.
the FEC node is named 'ethernet', and a PSC in uart mode is named 'serial'.
2. device_type property
-----------------------
@ -66,28 +66,47 @@ exactly.
Since device_type isn't enough to match devices to drivers, there also
needs to be a naming convention for the compatible property. Compatible
is an list of device descriptions sorted from specific to generic. For
the mpc52xx, the required format for each compatible value is
<chip>-<device>[-<mode>]. At the minimum, the list shall contain two
items; the first specifying the exact chip, and the second specifying
mpc52xx for the chip.
the mpc5200, the required format for each compatible value is
<chip>-<device>[-<mode>]. The OS should be able to match a device driver
to the device based solely on the compatible value. If two drivers
match on the compatible list; the 'most compatible' driver should be
selected.
ie. ethernet on mpc5200b: compatible = "mpc5200b-ethernet\0mpc52xx-ethernet"
The split between the MPC5200 and the MPC5200B leaves a bit of a
connundrum. How should the compatible property be set up to provide
maximum compatability information; but still acurately describe the
chip? For the MPC5200; the answer is easy. Most of the SoC devices
originally appeared on the MPC5200. Since they didn't exist anywhere
else; the 5200 compatible properties will contain only one item;
"mpc5200-<device>".
The idea here is that most drivers will match to the most generic field
in the compatible list (mpc52xx-*), but can also test the more specific
field for enabling bug fixes or extra features.
The 5200B is almost the same as the 5200, but not quite. It fixes
silicon bugs and it adds a small number of enhancements. Most of the
devices either provide exactly the same interface as on the 5200. A few
devices have extra functions but still have a backwards compatible mode.
To express this infomation as completely as possible, 5200B device trees
should have two items in the compatible list;
"mpc5200b-<device>\0mpc5200-<device>". It is *strongly* recommended
that 5200B device trees follow this convention (instead of only listing
the base mpc5200 item).
If another chip appear on the market with one of the mpc5200 SoC
devices, then the compatible list should include mpc5200-<device>.
ie. ethernet on mpc5200: compatible = "mpc5200-ethernet"
ethernet on mpc5200b: compatible = "mpc5200b-ethernet\0mpc5200-ethernet"
Modal devices, like PSCs, also append the configured function to the
end of the compatible field. ie. A PSC in i2s mode would specify
"mpc52xx-psc-i2s", not "mpc52xx-i2s". This convention is chosen to
"mpc5200-psc-i2s", not "mpc5200-i2s". This convention is chosen to
avoid naming conflicts with non-psc devices providing the same
function. For example, "mpc52xx-spi" and "mpc52xx-psc-spi" describe
function. For example, "mpc5200-spi" and "mpc5200-psc-spi" describe
the mpc5200 simple spi device and a PSC spi mode respectively.
If the soc device is more generic and present on other SOCs, the
compatible property can specify the more generic device type also.
ie. mscan: compatible = "mpc5200-mscan\0mpc52xx-mscan\0fsl,mscan";
ie. mscan: compatible = "mpc5200-mscan\0fsl,mscan";
At the time of writing, exact chip may be either 'mpc5200' or
'mpc5200b'.
@ -96,7 +115,7 @@ Device drivers should always try to match as generically as possible.
III - Structure
===============
The device tree for an mpc52xx board follows the structure defined in
The device tree for an mpc5200 board follows the structure defined in
booting-without-of.txt with the following additional notes:
0) the root node
@ -115,7 +134,7 @@ Typical memory description node; see booting-without-of.
3) The soc5200 node
-------------------
This node describes the on chip SOC peripherals. Every mpc52xx based
This node describes the on chip SOC peripherals. Every mpc5200 based
board will have this node, and as such there is a common naming
convention for SOC devices.
@ -125,71 +144,111 @@ name type description
device_type string must be "soc"
ranges int should be <0 baseaddr baseaddr+10000>
reg int must be <baseaddr 10000>
compatible string mpc5200: "mpc5200-soc"
mpc5200b: "mpc5200b-soc\0mpc5200-soc"
system-frequency int Fsystem frequency; source of all
other clocks.
bus-frequency int IPB bus frequency in HZ. Clock rate
used by most of the soc devices.
#interrupt-cells int must be <3>.
Recommended properties:
name type description
---- ---- -----------
compatible string should be "<chip>-soc\0mpc52xx-soc"
ie. "mpc5200b-soc\0mpc52xx-soc"
#interrupt-cells int must be <3>. If it is not defined
here then it must be defined in every
soc device node.
bus-frequency int IPB bus frequency in HZ. Clock rate
used by most of the soc devices.
Defining it here avoids needing it
added to every device node.
model string Exact model of the chip;
ie: model="fsl,mpc5200"
revision string Silicon revision of chip
ie: revision="M08A"
The 'model' and 'revision' properties are *strongly* recommended. Having
them presence acts as a bit of a safety net for working around as yet
undiscovered bugs on one version of silicon. For example, device drivers
can use the model and revision properties to decide if a bug fix should
be turned on.
4) soc5200 child nodes
----------------------
Any on chip SOC devices available to Linux must appear as soc5200 child nodes.
Note: in the tables below, '*' matches all <chip> values. ie.
*-pic would translate to "mpc5200-pic\0mpc52xx-pic"
Note: The tables below show the value for the mpc5200. A mpc5200b device
tree should use the "mpc5200b-<device>\0mpc5200-<device> form.
Required soc5200 child nodes:
name device_type compatible Description
---- ----------- ---------- -----------
cdm@<addr> cdm *-cmd Clock Distribution
pic@<addr> interrupt-controller *-pic need an interrupt
cdm@<addr> cdm mpc5200-cmd Clock Distribution
pic@<addr> interrupt-controller mpc5200-pic need an interrupt
controller to boot
bestcomm@<addr> dma-controller *-bestcomm 52xx pic also requires
the bestcomm device
bestcomm@<addr> dma-controller mpc5200-bestcomm 5200 pic also requires
the bestcomm device
Recommended soc5200 child nodes; populate as needed for your board
name device_type compatible Description
---- ----------- ---------- -----------
gpt@<addr> gpt *-gpt General purpose timers
rtc@<addr> rtc *-rtc Real time clock
mscan@<addr> mscan *-mscan CAN bus controller
pci@<addr> pci *-pci PCI bridge
serial@<addr> serial *-psc-uart PSC in serial mode
i2s@<addr> sound *-psc-i2s PSC in i2s mode
ac97@<addr> sound *-psc-ac97 PSC in ac97 mode
spi@<addr> spi *-psc-spi PSC in spi mode
irda@<addr> irda *-psc-irda PSC in IrDA mode
spi@<addr> spi *-spi MPC52xx spi device
ethernet@<addr> network *-fec MPC52xx ethernet device
ata@<addr> ata *-ata IDE ATA interface
i2c@<addr> i2c *-i2c I2C controller
usb@<addr> usb-ohci-be *-ohci,ohci-be USB controller
xlb@<addr> xlb *-xlb XLB arbritrator
name device_type compatible Description
---- ----------- ---------- -----------
gpt@<addr> gpt mpc5200-gpt General purpose timers
rtc@<addr> rtc mpc5200-rtc Real time clock
mscan@<addr> mscan mpc5200-mscan CAN bus controller
pci@<addr> pci mpc5200-pci PCI bridge
serial@<addr> serial mpc5200-psc-uart PSC in serial mode
i2s@<addr> sound mpc5200-psc-i2s PSC in i2s mode
ac97@<addr> sound mpc5200-psc-ac97 PSC in ac97 mode
spi@<addr> spi mpc5200-psc-spi PSC in spi mode
irda@<addr> irda mpc5200-psc-irda PSC in IrDA mode
spi@<addr> spi mpc5200-spi MPC5200 spi device
ethernet@<addr> network mpc5200-fec MPC5200 ethernet device
ata@<addr> ata mpc5200-ata IDE ATA interface
i2c@<addr> i2c mpc5200-i2c I2C controller
usb@<addr> usb-ohci-be mpc5200-ohci,ohci-be USB controller
xlb@<addr> xlb mpc5200-xlb XLB arbritrator
Important child node properties
name type description
---- ---- -----------
cell-index int When multiple devices are present, is the
index of the device in the hardware (ie. There
are 6 PSC on the 5200 numbered PSC1 to PSC6)
PSC1 has 'cell-index = <0>'
PSC4 has 'cell-index = <3>'
5) General Purpose Timer nodes (child of soc5200 node)
On the mpc5200 and 5200b, GPT0 has a watchdog timer function. If the board
design supports the internal wdt, then the device node for GPT0 should
include the empty property 'has-wdt'.
6) PSC nodes (child of soc5200 node)
PSC nodes can define the optional 'port-number' property to force assignment
order of serial ports. For example, PSC5 might be physically connected to
the port labeled 'COM1' and PSC1 wired to 'COM1'. In this case, PSC5 would
have a "port-number = <0>" property, and PSC1 would have "port-number = <1>".
PSC in i2s mode: The mpc5200 and mpc5200b PSCs are not compatible when in
i2s mode. An 'mpc5200b-psc-i2s' node cannot include 'mpc5200-psc-i2s' in the
compatible field.
IV - Extra Notes
================
1. Interrupt mapping
--------------------
The mpc52xx pic driver splits hardware IRQ numbers into two levels. The
The mpc5200 pic driver splits hardware IRQ numbers into two levels. The
split reflects the layout of the PIC hardware itself, which groups
interrupts into one of three groups; CRIT, MAIN or PERP. Also, the
Bestcomm dma engine has it's own set of interrupt sources which are
cascaded off of peripheral interrupt 0, which the driver interprets as a
fourth group, SDMA.
The interrupts property for device nodes using the mpc52xx pic consists
The interrupts property for device nodes using the mpc5200 pic consists
of three cells; <L1 L2 level>
L1 := [CRIT=0, MAIN=1, PERP=2, SDMA=3]
L2 := interrupt number; directly mapped from the value in the
"ICTL PerStat, MainStat, CritStat Encoded Register"
level := [LEVEL_HIGH=0, EDGE_RISING=1, EDGE_FALLING=2, LEVEL_LOW=3]
2. Shared registers
-------------------
Some SoC devices share registers between them. ie. the i2c devices use
a single clock control register, and almost all device are affected by
the port_config register. Devices which need to manipulate shared regs
should look to the parent SoC node. The soc node is responsible
for arbitrating all shared register access.

View File

@ -180,40 +180,81 @@ PCI
pci=lastbus=NUMBER Scan upto NUMBER busses, no matter what the mptable says.
pci=noacpi Don't use ACPI to set up PCI interrupt routing.
IOMMU
IOMMU (input/output memory management unit)
iommu=[size][,noagp][,off][,force][,noforce][,leak][,memaper[=order]][,merge]
[,forcesac][,fullflush][,nomerge][,noaperture][,calgary]
size set size of iommu (in bytes)
noagp don't initialize the AGP driver and use full aperture.
off don't use the IOMMU
leak turn on simple iommu leak tracing (only when CONFIG_IOMMU_LEAK is on)
memaper[=order] allocate an own aperture over RAM with size 32MB^order.
noforce don't force IOMMU usage. Default.
force Force IOMMU.
merge Do SG merging. Implies force (experimental)
nomerge Don't do SG merging.
forcesac For SAC mode for masks <40bits (experimental)
fullflush Flush IOMMU on each allocation (default)
nofullflush Don't use IOMMU fullflush
allowed overwrite iommu off workarounds for specific chipsets.
soft Use software bounce buffering (default for Intel machines)
noaperture Don't touch the aperture for AGP.
allowdac Allow DMA >4GB
When off all DMA over >4GB is forced through an IOMMU or bounce
buffering.
nodac Forbid DMA >4GB
panic Always panic when IOMMU overflows
calgary Use the Calgary IOMMU if it is available
Currently four x86-64 PCI-DMA mapping implementations exist:
swiotlb=pages[,force]
1. <arch/x86_64/kernel/pci-nommu.c>: use no hardware/software IOMMU at all
(e.g. because you have < 3 GB memory).
Kernel boot message: "PCI-DMA: Disabling IOMMU"
pages Prereserve that many 128K pages for the software IO bounce buffering.
force Force all IO through the software TLB.
2. <arch/x86_64/kernel/pci-gart.c>: AMD GART based hardware IOMMU.
Kernel boot message: "PCI-DMA: using GART IOMMU"
calgary=[64k,128k,256k,512k,1M,2M,4M,8M]
calgary=[translate_empty_slots]
calgary=[disable=<PCI bus number>]
3. <arch/x86_64/kernel/pci-swiotlb.c> : Software IOMMU implementation. Used
e.g. if there is no hardware IOMMU in the system and it is need because
you have >3GB memory or told the kernel to us it (iommu=soft))
Kernel boot message: "PCI-DMA: Using software bounce buffering
for IO (SWIOTLB)"
4. <arch/x86_64/pci-calgary.c> : IBM Calgary hardware IOMMU. Used in IBM
pSeries and xSeries servers. This hardware IOMMU supports DMA address
mapping with memory protection, etc.
Kernel boot message: "PCI-DMA: Using Calgary IOMMU"
iommu=[<size>][,noagp][,off][,force][,noforce][,leak[=<nr_of_leak_pages>]
[,memaper[=<order>]][,merge][,forcesac][,fullflush][,nomerge]
[,noaperture][,calgary]
General iommu options:
off Don't initialize and use any kind of IOMMU.
noforce Don't force hardware IOMMU usage when it is not needed.
(default).
force Force the use of the hardware IOMMU even when it is
not actually needed (e.g. because < 3 GB memory).
soft Use software bounce buffering (SWIOTLB) (default for
Intel machines). This can be used to prevent the usage
of an available hardware IOMMU.
iommu options only relevant to the AMD GART hardware IOMMU:
<size> Set the size of the remapping area in bytes.
allowed Overwrite iommu off workarounds for specific chipsets.
fullflush Flush IOMMU on each allocation (default).
nofullflush Don't use IOMMU fullflush.
leak Turn on simple iommu leak tracing (only when
CONFIG_IOMMU_LEAK is on). Default number of leak pages
is 20.
memaper[=<order>] Allocate an own aperture over RAM with size 32MB<<order.
(default: order=1, i.e. 64MB)
merge Do scatter-gather (SG) merging. Implies "force"
(experimental).
nomerge Don't do scatter-gather (SG) merging.
noaperture Ask the IOMMU not to touch the aperture for AGP.
forcesac Force single-address cycle (SAC) mode for masks <40bits
(experimental).
noagp Don't initialize the AGP driver and use full aperture.
allowdac Allow double-address cycle (DAC) mode, i.e. DMA >4GB.
DAC is used with 32-bit PCI to push a 64-bit address in
two cycles. When off all DMA over >4GB is forced through
an IOMMU or software bounce buffering.
nodac Forbid DAC mode, i.e. DMA >4GB.
panic Always panic when IOMMU overflows.
calgary Use the Calgary IOMMU if it is available
iommu options only relevant to the software bounce buffering (SWIOTLB) IOMMU
implementation:
swiotlb=<pages>[,force]
<pages> Prereserve that many 128K pages for the software IO
bounce buffering.
force Force all IO through the software TLB.
Settings for the IBM Calgary hardware IOMMU currently found in IBM
pSeries and xSeries machines:
calgary=[64k,128k,256k,512k,1M,2M,4M,8M]
calgary=[translate_empty_slots]
calgary=[disable=<PCI bus number>]
panic Always panic when IOMMU overflows
64k,...,8M - Set the size of each PCI slot's translation table
when using the Calgary IOMMU. This is the size of the translation
@ -234,14 +275,14 @@ IOMMU
Debugging
oops=panic Always panic on oopses. Default is to just kill the process,
but there is a small probability of deadlocking the machine.
This will also cause panics on machine check exceptions.
Useful together with panic=30 to trigger a reboot.
oops=panic Always panic on oopses. Default is to just kill the process,
but there is a small probability of deadlocking the machine.
This will also cause panics on machine check exceptions.
Useful together with panic=30 to trigger a reboot.
kstack=N Print that many words from the kernel stack in oops dumps.
kstack=N Print N words from the kernel stack in oops dumps.
pagefaulttrace Dump all page faults. Only useful for extreme debugging
pagefaulttrace Dump all page faults. Only useful for extreme debugging
and will create a lot of output.
call_trace=[old|both|newfallback|new]
@ -251,15 +292,8 @@ Debugging
newfallback: use new unwinder but fall back to old if it gets
stuck (default)
call_trace=[old|both|newfallback|new]
old: use old inexact backtracer
new: use new exact dwarf2 unwinder
both: print entries from both
newfallback: use new unwinder but fall back to old if it gets
stuck (default)
Misc
Miscellaneous
noreplacement Don't replace instructions with more appropriate ones
for the CPU. This may be useful on asymmetric MP systems
where some CPU have less capabilities than the others.
where some CPUs have less capabilities than others.

View File

@ -2,7 +2,7 @@ Firmware support for CPU hotplug under Linux/x86-64
---------------------------------------------------
Linux/x86-64 supports CPU hotplug now. For various reasons Linux wants to
know in advance boot time the maximum number of CPUs that could be plugged
know in advance of boot time the maximum number of CPUs that could be plugged
into the system. ACPI 3.0 currently has no official way to supply
this information from the firmware to the operating system.

View File

@ -9,9 +9,9 @@ zombie. While the thread is in user space the kernel stack is empty
except for the thread_info structure at the bottom.
In addition to the per thread stacks, there are specialized stacks
associated with each cpu. These stacks are only used while the kernel
is in control on that cpu, when a cpu returns to user space the
specialized stacks contain no useful data. The main cpu stacks is
associated with each CPU. These stacks are only used while the kernel
is in control on that CPU; when a CPU returns to user space the
specialized stacks contain no useful data. The main CPU stacks are:
* Interrupt stack. IRQSTACKSIZE
@ -32,17 +32,17 @@ x86_64 also has a feature which is not available on i386, the ability
to automatically switch to a new stack for designated events such as
double fault or NMI, which makes it easier to handle these unusual
events on x86_64. This feature is called the Interrupt Stack Table
(IST). There can be up to 7 IST entries per cpu. The IST code is an
index into the Task State Segment (TSS), the IST entries in the TSS
point to dedicated stacks, each stack can be a different size.
(IST). There can be up to 7 IST entries per CPU. The IST code is an
index into the Task State Segment (TSS). The IST entries in the TSS
point to dedicated stacks; each stack can be a different size.
An IST is selected by an non-zero value in the IST field of an
An IST is selected by a non-zero value in the IST field of an
interrupt-gate descriptor. When an interrupt occurs and the hardware
loads such a descriptor, the hardware automatically sets the new stack
pointer based on the IST value, then invokes the interrupt handler. If
software wants to allow nested IST interrupts then the handler must
adjust the IST values on entry to and exit from the interrupt handler.
(this is occasionally done, e.g. for debug exceptions)
(This is occasionally done, e.g. for debug exceptions.)
Events with different IST codes (i.e. with different stacks) can be
nested. For example, a debug interrupt can safely be interrupted by an
@ -58,17 +58,17 @@ The currently assigned IST stacks are :-
Used for interrupt 12 - Stack Fault Exception (#SS).
This allows to recover from invalid stack segments. Rarely
This allows the CPU to recover from invalid stack segments. Rarely
happens.
* DOUBLEFAULT_STACK. EXCEPTION_STKSZ (PAGE_SIZE).
Used for interrupt 8 - Double Fault Exception (#DF).
Invoked when handling a exception causes another exception. Happens
when the kernel is very confused (e.g. kernel stack pointer corrupt)
Using a separate stack allows to recover from it well enough in many
cases to still output an oops.
Invoked when handling one exception causes another exception. Happens
when the kernel is very confused (e.g. kernel stack pointer corrupt).
Using a separate stack allows the kernel to recover from it well enough
in many cases to still output an oops.
* NMI_STACK. EXCEPTION_STKSZ (PAGE_SIZE).

View File

@ -0,0 +1,70 @@
Configurable sysfs parameters for the x86-64 machine check code.
Machine checks report internal hardware error conditions detected
by the CPU. Uncorrected errors typically cause a machine check
(often with panic), corrected ones cause a machine check log entry.
Machine checks are organized in banks (normally associated with
a hardware subsystem) and subevents in a bank. The exact meaning
of the banks and subevent is CPU specific.
mcelog knows how to decode them.
When you see the "Machine check errors logged" message in the system
log then mcelog should run to collect and decode machine check entries
from /dev/mcelog. Normally mcelog should be run regularly from a cronjob.
Each CPU has a directory in /sys/devices/system/machinecheck/machinecheckN
(N = CPU number)
The directory contains some configurable entries:
Entries:
bankNctl
(N bank number)
64bit Hex bitmask enabling/disabling specific subevents for bank N
When a bit in the bitmask is zero then the respective
subevent will not be reported.
By default all events are enabled.
Note that BIOS maintain another mask to disable specific events
per bank. This is not visible here
The following entries appear for each CPU, but they are truly shared
between all CPUs.
check_interval
How often to poll for corrected machine check errors, in seconds
(Note output is hexademical). Default 5 minutes.
tolerant
Tolerance level. When a machine check exception occurs for a non
corrected machine check the kernel can take different actions.
Since machine check exceptions can happen any time it is sometimes
risky for the kernel to kill a process because it defies
normal kernel locking rules. The tolerance level configures
how hard the kernel tries to recover even at some risk of deadlock.
0: always panic,
1: panic if deadlock possible,
2: try to avoid panic,
3: never panic or exit (for testing only)
Default: 1
Note this only makes a difference if the CPU allows recovery
from a machine check exception. Current x86 CPUs generally do not.
trigger
Program to run when a machine check event is detected.
This is an alternative to running mcelog regularly from cron
and allows to detect events faster.
TBD document entries for AMD threshold interrupt configuration
For more details about the x86 machine check architecture
see the Intel and AMD architecture manuals from their developer websites.
For more details about the architecture see
see http://one.firstfloor.org/~andi/mce.pdf

View File

@ -3,26 +3,26 @@
Virtual memory map with 4 level page tables:
0000000000000000 - 00007fffffffffff (=47bits) user space, different per mm
0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm
hole caused by [48:63] sign extension
ffff800000000000 - ffff80ffffffffff (=40bits) guard hole
ffff810000000000 - ffffc0ffffffffff (=46bits) direct mapping of all phys. memory
ffffc10000000000 - ffffc1ffffffffff (=40bits) hole
ffffc20000000000 - ffffe1ffffffffff (=45bits) vmalloc/ioremap space
ffff800000000000 - ffff80ffffffffff (=40 bits) guard hole
ffff810000000000 - ffffc0ffffffffff (=46 bits) direct mapping of all phys. memory
ffffc10000000000 - ffffc1ffffffffff (=40 bits) hole
ffffc20000000000 - ffffe1ffffffffff (=45 bits) vmalloc/ioremap space
... unused hole ...
ffffffff80000000 - ffffffff82800000 (=40MB) kernel text mapping, from phys 0
ffffffff80000000 - ffffffff82800000 (=40 MB) kernel text mapping, from phys 0
... unused hole ...
ffffffff88000000 - fffffffffff00000 (=1919MB) module mapping space
ffffffff88000000 - fffffffffff00000 (=1919 MB) module mapping space
The direct mapping covers all memory in the system upto the highest
The direct mapping covers all memory in the system up to the highest
memory address (this means in some cases it can also include PCI memory
holes)
holes).
vmalloc space is lazily synchronized into the different PML4 pages of
the processes using the page fault handler, with init_level4_pgt as
reference.
Current X86-64 implementations only support 40 bit of address space,
but we support upto 46bits. This expands into MBZ space in the page tables.
Current X86-64 implementations only support 40 bits of address space,
but we support up to 46 bits. This expands into MBZ space in the page tables.
-Andi Kleen, Jul 2004

View File

@ -620,6 +620,11 @@ P: Haavard Skinnemoen
M: hskinnemoen@atmel.com
S: Supported
ATMEL SPI DRIVER
P: Haavard Skinnemoen
M: hskinnemoen@atmel.com
S: Supported
ATMEL WIRELESS DRIVER
P: Simon Kelley
M: simon@thekelleys.org.uk
@ -2523,6 +2528,12 @@ M: olof@lixom.net
L: netdev@vger.kernel.org
S: Maintained
PA SEMI SMBUS DRIVER
P: Olof Johansson
M: olof@lixom.net
L: i2c@lm-sensors.org
S: Maintained
PARALLEL PORT SUPPORT
P: Phil Blundell
M: philb@gnu.org
@ -3768,6 +3779,7 @@ P: Andi Kleen
M: ak@suse.de
L: discuss@x86-64.org
W: http://www.x86-64.org
T: quilt ftp://ftp.firstfloor.org/pub/ak/x86_64/quilt-current
S: Maintained
YAM DRIVER FOR AX.25

View File

@ -825,9 +825,6 @@ include/config/kernel.release: include/config/auto.conf FORCE
# Listed in dependency order
PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3
# prepare-all is deprecated, use prepare as valid replacement
PHONY += prepare-all
# prepare3 is used to check if we are building in a separate output directory,
# and if so do:
# 1) Check that make has not been executed in the kernel src $(srctree)
@ -860,7 +857,7 @@ prepare0: archprepare FORCE
$(Q)$(MAKE) $(build)=.
# All the preparing..
prepare prepare-all: prepare0
prepare: prepare0
# Leave this as default for preprocessing vmlinux.lds.S, which is now
# done in arch/$(ARCH)/kernel/Makefile
@ -931,6 +928,12 @@ headers_install: include/linux/version.h scripts_basic FORCE
$(Q)$(MAKE) $(build)=scripts scripts/unifdef
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.headersinst obj=include
PHONY += headers_check_all
headers_check_all: headers_install_all
$(Q)for arch in $(HDRARCHES); do \
$(MAKE) ARCH=$$arch -f $(srctree)/scripts/Makefile.headersinst obj=include BIASMDIR=-bi-$$arch HDRCHECK=1 ;\
done
PHONY += headers_check
headers_check: headers_install
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.headersinst obj=include HDRCHECK=1

View File

@ -159,8 +159,7 @@ void __init init_IRQ(void)
int irq;
for (irq = 0; irq < NR_IRQS; irq++)
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_DELAYED_DISABLE |
IRQ_NOPROBE;
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
#ifdef CONFIG_SMP
bad_irq_desc.affinity = CPU_MASK_ALL;

View File

@ -70,5 +70,5 @@ register_isa_ports(unsigned int membase, unsigned int portbase, unsigned int por
isa_membase = membase;
isa_portbase = portbase;
isa_portshift = portshift;
isa_sysctl_header = register_sysctl_table(ctl_bus, 0);
isa_sysctl_header = register_sysctl_table(ctl_bus);
}

View File

@ -87,7 +87,7 @@ static struct clocksource clocksource_imx = {
.read = imx_get_cycles,
.mask = 0xFFFFFFFF,
.shift = 20,
.is_continuous = 1,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int __init imx_clocksource_init(void)

View File

@ -395,7 +395,7 @@ static struct clocksource clocksource_ixp4xx = {
.read = ixp4xx_get_cycles,
.mask = CLOCKSOURCE_MASK(32),
.shift = 20,
.is_continuous = 1,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
unsigned long ixp4xx_timer_freq = FREQ;

View File

@ -62,7 +62,7 @@ static struct clocksource clocksource_netx = {
.read = netx_get_cycles,
.mask = CLOCKSOURCE_MASK(32),
.shift = 20,
.is_continuous = 1,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
/*

View File

@ -112,7 +112,7 @@ static struct clocksource clocksource_pxa = {
.read = pxa_get_cycles,
.mask = CLOCKSOURCE_MASK(32),
.shift = 20,
.is_continuous = 1,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static void __init pxa_timer_init(void)

View File

@ -8,7 +8,6 @@
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/kernel.h>
@ -36,12 +35,11 @@ static struct eth_addr __initdata hw_addr[2];
static struct eth_platform_data __initdata eth_data[2];
extern struct lcdc_platform_data atstk1000_fb0_data;
static struct spi_board_info spi_board_info[] __initdata = {
static struct spi_board_info spi0_board_info[] __initdata = {
{
/* QVGA display */
.modalias = "ltv350qv",
.controller_data = (void *)GPIO_PIN_PA(4),
.max_speed_hz = 16000000,
.bus_num = 0,
.chip_select = 1,
},
};
@ -149,8 +147,7 @@ static int __init atstk1002_init(void)
set_hw_addr(at32_add_device_eth(0, &eth_data[0]));
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
at32_add_device_spi(0);
at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info));
at32_add_device_lcdc(0, &atstk1000_fb0_data);
return 0;

View File

@ -8,14 +8,6 @@
* published by the Free Software Foundation.
*/
#if !defined(CONFIG_NFSD) && !defined(CONFIG_NFSD_MODULE)
#define sys_nfsservctl sys_ni_syscall
#endif
#if !defined(CONFIG_SYSV_IPC)
# define sys_ipc sys_ni_syscall
#endif
.section .rodata,"a",@progbits
.type sys_call_table,@object
.global sys_call_table
@ -129,7 +121,7 @@ sys_call_table:
.long sys_getitimer /* 105 */
.long sys_swapoff
.long sys_sysinfo
.long sys_ipc
.long sys_ni_syscall /* was sys_ipc briefly */
.long sys_sendfile
.long sys_setdomainname /* 110 */
.long sys_newuname
@ -287,4 +279,16 @@ sys_call_table:
.long sys_tee
.long sys_vmsplice
.long __sys_epoll_pwait /* 265 */
.long sys_msgget
.long sys_msgsnd
.long sys_msgrcv
.long sys_msgctl
.long sys_semget /* 270 */
.long sys_semop
.long sys_semctl
.long sys_semtimedop
.long sys_shmat
.long sys_shmget /* 275 */
.long sys_shmdt
.long sys_shmctl
.long sys_ni_syscall /* r8 is saturated at nr_syscalls */

View File

@ -37,7 +37,7 @@ static struct clocksource clocksource_avr32 = {
.read = read_cycle_count,
.mask = CLOCKSOURCE_MASK(32),
.shift = 16,
.is_continuous = 1,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
/*

View File

@ -8,6 +8,7 @@
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <asm/io.h>
@ -310,8 +311,6 @@ static void genclk_mode(struct clk *clk, int enabled)
{
u32 control;
BUG_ON(clk->index > 7);
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
if (enabled)
control |= SM_BIT(CEN);
@ -325,11 +324,6 @@ static unsigned long genclk_get_rate(struct clk *clk)
u32 control;
unsigned long div = 1;
BUG_ON(clk->index > 7);
if (!clk->parent)
return 0;
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
if (control & SM_BIT(DIVEN))
div = 2 * (SM_BFEXT(DIV, control) + 1);
@ -342,11 +336,6 @@ static long genclk_set_rate(struct clk *clk, unsigned long rate, int apply)
u32 control;
unsigned long parent_rate, actual_rate, div;
BUG_ON(clk->index > 7);
if (!clk->parent)
return 0;
parent_rate = clk->parent->get_rate(clk->parent);
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
@ -373,11 +362,8 @@ int genclk_set_parent(struct clk *clk, struct clk *parent)
{
u32 control;
BUG_ON(clk->index > 7);
printk("clk %s: new parent %s (was %s)\n",
clk->name, parent->name,
clk->parent ? clk->parent->name : "(null)");
clk->name, parent->name, clk->parent->name);
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
@ -399,6 +385,22 @@ int genclk_set_parent(struct clk *clk, struct clk *parent)
return 0;
}
static void __init genclk_init_parent(struct clk *clk)
{
u32 control;
struct clk *parent;
BUG_ON(clk->index > 7);
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
if (control & SM_BIT(OSCSEL))
parent = (control & SM_BIT(PLLSEL)) ? &pll1 : &osc1;
else
parent = (control & SM_BIT(PLLSEL)) ? &pll0 : &osc0;
clk->parent = parent;
}
/* --------------------------------------------------------------------
* System peripherals
* -------------------------------------------------------------------- */
@ -750,8 +752,41 @@ static struct resource atmel_spi1_resource[] = {
DEFINE_DEV(atmel_spi, 1);
DEV_CLK(spi_clk, atmel_spi1, pba, 1);
struct platform_device *__init at32_add_device_spi(unsigned int id)
static void
at32_spi_setup_slaves(unsigned int bus_num, struct spi_board_info *b,
unsigned int n, const u8 *pins)
{
unsigned int pin, mode;
for (; n; n--, b++) {
b->bus_num = bus_num;
if (b->chip_select >= 4)
continue;
pin = (unsigned)b->controller_data;
if (!pin) {
pin = pins[b->chip_select];
b->controller_data = (void *)pin;
}
mode = AT32_GPIOF_OUTPUT;
if (!(b->mode & SPI_CS_HIGH))
mode |= AT32_GPIOF_HIGH;
at32_select_gpio(pin, mode);
}
}
struct platform_device *__init
at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n)
{
/*
* Manage the chipselects as GPIOs, normally using the same pins
* the SPI controller expects; but boards can use other pins.
*/
static u8 __initdata spi0_pins[] =
{ GPIO_PIN_PA(3), GPIO_PIN_PA(4),
GPIO_PIN_PA(5), GPIO_PIN_PA(20), };
static u8 __initdata spi1_pins[] =
{ GPIO_PIN_PB(2), GPIO_PIN_PB(3),
GPIO_PIN_PB(4), GPIO_PIN_PA(27), };
struct platform_device *pdev;
switch (id) {
@ -760,14 +795,7 @@ struct platform_device *__init at32_add_device_spi(unsigned int id)
select_peripheral(PA(0), PERIPH_A, 0); /* MISO */
select_peripheral(PA(1), PERIPH_A, 0); /* MOSI */
select_peripheral(PA(2), PERIPH_A, 0); /* SCK */
/* NPCS[2:0] */
at32_select_gpio(GPIO_PIN_PA(3),
AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
at32_select_gpio(GPIO_PIN_PA(4),
AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
at32_select_gpio(GPIO_PIN_PA(5),
AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
at32_spi_setup_slaves(0, b, n, spi0_pins);
break;
case 1:
@ -775,20 +803,14 @@ struct platform_device *__init at32_add_device_spi(unsigned int id)
select_peripheral(PB(0), PERIPH_B, 0); /* MISO */
select_peripheral(PB(1), PERIPH_B, 0); /* MOSI */
select_peripheral(PB(5), PERIPH_B, 0); /* SCK */
/* NPCS[2:0] */
at32_select_gpio(GPIO_PIN_PB(2),
AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
at32_select_gpio(GPIO_PIN_PB(3),
AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
at32_select_gpio(GPIO_PIN_PB(4),
AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
at32_spi_setup_slaves(1, b, n, spi1_pins);
break;
default:
return NULL;
}
spi_register_board_info(b, n);
platform_device_register(pdev);
return pdev;
}
@ -872,6 +894,50 @@ at32_add_device_lcdc(unsigned int id, struct lcdc_platform_data *data)
return pdev;
}
/* --------------------------------------------------------------------
* GCLK
* -------------------------------------------------------------------- */
static struct clk gclk0 = {
.name = "gclk0",
.mode = genclk_mode,
.get_rate = genclk_get_rate,
.set_rate = genclk_set_rate,
.set_parent = genclk_set_parent,
.index = 0,
};
static struct clk gclk1 = {
.name = "gclk1",
.mode = genclk_mode,
.get_rate = genclk_get_rate,
.set_rate = genclk_set_rate,
.set_parent = genclk_set_parent,
.index = 1,
};
static struct clk gclk2 = {
.name = "gclk2",
.mode = genclk_mode,
.get_rate = genclk_get_rate,
.set_rate = genclk_set_rate,
.set_parent = genclk_set_parent,
.index = 2,
};
static struct clk gclk3 = {
.name = "gclk3",
.mode = genclk_mode,
.get_rate = genclk_get_rate,
.set_rate = genclk_set_rate,
.set_parent = genclk_set_parent,
.index = 3,
};
static struct clk gclk4 = {
.name = "gclk4",
.mode = genclk_mode,
.get_rate = genclk_get_rate,
.set_rate = genclk_set_rate,
.set_parent = genclk_set_parent,
.index = 4,
};
struct clk *at32_clock_list[] = {
&osc32k,
&osc0,
@ -908,6 +974,11 @@ struct clk *at32_clock_list[] = {
&atmel_spi1_spi_clk,
&lcdc0_hclk,
&lcdc0_pixclk,
&gclk0,
&gclk1,
&gclk2,
&gclk3,
&gclk4,
};
unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list);
@ -936,6 +1007,13 @@ void __init at32_clock_init(void)
if (sm_readl(sm, PM_PLL1) & SM_BIT(PLLOSC))
pll1.parent = &osc1;
genclk_init_parent(&gclk0);
genclk_init_parent(&gclk1);
genclk_init_parent(&gclk2);
genclk_init_parent(&gclk3);
genclk_init_parent(&gclk4);
genclk_init_parent(&lcdc0_pixclk);
/*
* Turn on all clocks that have at least one user already, and
* turn off everything else. We only do this for module

View File

@ -63,7 +63,11 @@ EXPORT_SYMBOL(clk_enable);
static void __clk_disable(struct clk *clk)
{
BUG_ON(clk->users == 0);
if (clk->users == 0) {
printk(KERN_ERR "%s: mismatched disable\n", clk->name);
WARN_ON(1);
return;
}
if (--clk->users == 0 && clk->mode)
clk->mode(clk, 0);

View File

@ -125,7 +125,6 @@ unsigned long sleep_phys_sp(void *sp)
* Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6
* when all the PM interfaces exist nicely.
*/
#define CTL_PM 9899
#define CTL_PM_SUSPEND 1
#define CTL_PM_CMODE 2
#define CTL_PM_P0 4
@ -402,17 +401,53 @@ static int cm_sysctl(ctl_table *table, int __user *name, int nlen,
static struct ctl_table pm_table[] =
{
{CTL_PM_SUSPEND, "suspend", NULL, 0, 0200, NULL, &sysctl_pm_do_suspend},
{CTL_PM_CMODE, "cmode", &clock_cmode_current, sizeof(int), 0644, NULL, &cmode_procctl, &cmode_sysctl, NULL},
{CTL_PM_P0, "p0", &clock_p0_current, sizeof(int), 0644, NULL, &p0_procctl, &p0_sysctl, NULL},
{CTL_PM_CM, "cm", &clock_cm_current, sizeof(int), 0644, NULL, &cm_procctl, &cm_sysctl, NULL},
{0}
{
.ctl_name = CTL_PM_SUSPEND,
.procname = "suspend",
.data = NULL,
.maxlen = 0,
.mode = 0200,
.proc_handler = &sysctl_pm_do_suspend,
},
{
.ctl_name = CTL_PM_CMODE,
.procname = "cmode",
.data = &clock_cmode_current,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &cmode_procctl,
.strategy = &cmode_sysctl,
},
{
.ctl_name = CTL_PM_P0,
.procname = "p0",
.data = &clock_p0_current,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &p0_procctl,
.strategy = &p0_sysctl,
},
{
.ctl_name = CTL_PM_CM,
.procname = "cm",
.data = &clock_cm_current,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &cm_procctl,
.strategy = &cm_sysctl,
},
{ .ctl_name = 0}
};
static struct ctl_table pm_dir_table[] =
{
{CTL_PM, "pm", NULL, 0, 0555, pm_table},
{0}
{
.ctl_name = CTL_PM,
.procname = "pm",
.mode = 0555,
.child = pm_table,
},
{ .ctl_name = 0}
};
/*
@ -420,7 +455,7 @@ static struct ctl_table pm_dir_table[] =
*/
static int __init pm_init(void)
{
register_sysctl_table(pm_dir_table, 1);
register_sysctl_table(pm_dir_table);
return 0;
}

View File

@ -175,22 +175,40 @@ static int procctl_frv_pin_cxnr(ctl_table *table, int write, struct file *filp,
*/
static struct ctl_table frv_table[] =
{
{ 1, "cache-mode", NULL, 0, 0644, NULL, &procctl_frv_cachemode },
{
.ctl_name = 1,
.procname = "cache-mode",
.data = NULL,
.maxlen = 0,
.mode = 0644,
.proc_handler = &procctl_frv_cachemode,
},
#ifdef CONFIG_MMU
{ 2, "pin-cxnr", NULL, 0, 0644, NULL, &procctl_frv_pin_cxnr },
{
.ctl_name = 2,
.procname = "pin-cxnr",
.data = NULL,
.maxlen = 0,
.mode = 0644,
.proc_handler = &procctl_frv_pin_cxnr
},
#endif
{ 0 }
{}
};
/*
* Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6
* when all the PM interfaces exist nicely.
*/
#define CTL_FRV 9898
static struct ctl_table frv_dir_table[] =
{
{CTL_FRV, "frv", NULL, 0, 0555, frv_table},
{0}
{
.ctl_name = CTL_FRV,
.procname = "frv",
.mode = 0555,
.child = frv_table
},
{}
};
/*
@ -198,7 +216,7 @@ static struct ctl_table frv_dir_table[] =
*/
static int __init frv_sysctl_init(void)
{
register_sysctl_table(frv_dir_table, 1);
register_sysctl_table(frv_dir_table);
return 0;
}

View File

@ -18,6 +18,18 @@ config GENERIC_TIME
bool
default y
config CLOCKSOURCE_WATCHDOG
bool
default y
config GENERIC_CLOCKEVENTS
bool
default y
config GENERIC_CLOCKEVENTS_BROADCAST
bool
default y
config LOCKDEP_SUPPORT
bool
default y
@ -74,6 +86,8 @@ source "init/Kconfig"
menu "Processor type and features"
source "kernel/time/Kconfig"
config SMP
bool "Symmetric multi-processing support"
---help---
@ -203,6 +217,15 @@ config PARAVIRT
However, when run without a hypervisor the kernel is
theoretically slower. If in doubt, say N.
config VMI
bool "VMI Paravirt-ops support"
depends on PARAVIRT && !NO_HZ
default y
help
VMI provides a paravirtualized interface to multiple hypervisors
include VMware ESX server and Xen by connecting to a ROM module
provided by the hypervisor.
config ACPI_SRAT
bool
default y
@ -1263,3 +1286,12 @@ config X86_TRAMPOLINE
config KTIME_SCALAR
bool
default y
config NO_IDLE_HZ
bool
depends on PARAVIRT
default y
help
Switches the regular HZ timer off when the system is going idle.
This helps a hypervisor detect that the Linux system is idle,
reducing the overhead of idle systems.

View File

@ -226,11 +226,6 @@ config X86_CMPXCHG
depends on !M386
default y
config X86_XADD
bool
depends on !M386
default y
config X86_L1_CACHE_SHIFT
int
default "7" if MPENTIUM4 || X86_GENERIC

View File

@ -87,7 +87,7 @@ config DOUBLEFAULT
config DEBUG_PARAVIRT
bool "Enable some paravirtualization debugging"
default y
default n
depends on PARAVIRT && DEBUG_KERNEL
help
Currently deliberately clobbers regs which are allowed to be

View File

@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.20-rc3
# Fri Jan 5 11:54:46 2007
# Linux kernel version: 2.6.20-git8
# Tue Feb 13 11:25:18 2007
#
CONFIG_X86_32=y
CONFIG_GENERIC_TIME=y
@ -10,6 +10,7 @@ CONFIG_STACKTRACE_SUPPORT=y
CONFIG_SEMAPHORE_SLEEPERS=y
CONFIG_X86=y
CONFIG_MMU=y
CONFIG_ZONE_DMA=y
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_IOMAP=y
CONFIG_GENERIC_BUG=y
@ -139,7 +140,6 @@ CONFIG_MPENTIUMIII=y
# CONFIG_MVIAC3_2 is not set
CONFIG_X86_GENERIC=y
CONFIG_X86_CMPXCHG=y
CONFIG_X86_XADD=y
CONFIG_X86_L1_CACHE_SHIFT=7
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
@ -198,6 +198,7 @@ CONFIG_FLAT_NODE_MEM_MAP=y
# CONFIG_SPARSEMEM_STATIC is not set
CONFIG_SPLIT_PTLOCK_CPUS=4
CONFIG_RESOURCES_64BIT=y
CONFIG_ZONE_DMA_FLAG=1
# CONFIG_HIGHPTE is not set
# CONFIG_MATH_EMULATION is not set
CONFIG_MTRR=y
@ -211,6 +212,7 @@ CONFIG_HZ_250=y
CONFIG_HZ=250
# CONFIG_KEXEC is not set
# CONFIG_CRASH_DUMP is not set
CONFIG_PHYSICAL_START=0x100000
# CONFIG_RELOCATABLE is not set
CONFIG_PHYSICAL_ALIGN=0x100000
# CONFIG_HOTPLUG_CPU is not set
@ -229,13 +231,14 @@ CONFIG_PM_SYSFS_DEPRECATED=y
# ACPI (Advanced Configuration and Power Interface) Support
#
CONFIG_ACPI=y
CONFIG_ACPI_PROCFS=y
CONFIG_ACPI_AC=y
CONFIG_ACPI_BATTERY=y
CONFIG_ACPI_BUTTON=y
# CONFIG_ACPI_VIDEO is not set
# CONFIG_ACPI_HOTKEY is not set
CONFIG_ACPI_FAN=y
# CONFIG_ACPI_DOCK is not set
# CONFIG_ACPI_BAY is not set
CONFIG_ACPI_PROCESSOR=y
CONFIG_ACPI_THERMAL=y
# CONFIG_ACPI_ASUS is not set
@ -306,7 +309,6 @@ CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
# CONFIG_PCIEPORTBUS is not set
CONFIG_PCI_MSI=y
# CONFIG_PCI_MULTITHREAD_PROBE is not set
# CONFIG_PCI_DEBUG is not set
# CONFIG_HT_IRQ is not set
CONFIG_ISA_DMA_API=y
@ -347,6 +349,7 @@ CONFIG_UNIX=y
CONFIG_XFRM=y
# CONFIG_XFRM_USER is not set
# CONFIG_XFRM_SUB_POLICY is not set
# CONFIG_XFRM_MIGRATE is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
@ -446,6 +449,7 @@ CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
CONFIG_FW_LOADER=y
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_SYS_HYPERVISOR is not set
#
@ -466,8 +470,7 @@ CONFIG_FW_LOADER=y
#
# Plug and Play support
#
CONFIG_PNP=y
CONFIG_PNPACPI=y
# CONFIG_PNP is not set
#
# Block devices
@ -515,6 +518,7 @@ CONFIG_BLK_DEV_IDECD=y
# CONFIG_BLK_DEV_IDETAPE is not set
# CONFIG_BLK_DEV_IDEFLOPPY is not set
# CONFIG_BLK_DEV_IDESCSI is not set
CONFIG_BLK_DEV_IDEACPI=y
# CONFIG_IDE_TASK_IOCTL is not set
#
@ -547,6 +551,7 @@ CONFIG_BLK_DEV_AMD74XX=y
# CONFIG_BLK_DEV_JMICRON is not set
# CONFIG_BLK_DEV_SC1200 is not set
CONFIG_BLK_DEV_PIIX=y
# CONFIG_BLK_DEV_IT8213 is not set
# CONFIG_BLK_DEV_IT821X is not set
# CONFIG_BLK_DEV_NS87415 is not set
# CONFIG_BLK_DEV_PDC202XX_OLD is not set
@ -557,6 +562,7 @@ CONFIG_BLK_DEV_PIIX=y
# CONFIG_BLK_DEV_SLC90E66 is not set
# CONFIG_BLK_DEV_TRM290 is not set
# CONFIG_BLK_DEV_VIA82CXXX is not set
# CONFIG_BLK_DEV_TC86C001 is not set
# CONFIG_IDE_ARM is not set
CONFIG_BLK_DEV_IDEDMA=y
# CONFIG_IDEDMA_IVB is not set
@ -655,6 +661,7 @@ CONFIG_AIC79XX_DEBUG_MASK=0
# Serial ATA (prod) and Parallel ATA (experimental) drivers
#
CONFIG_ATA=y
# CONFIG_ATA_NONSTANDARD is not set
CONFIG_SATA_AHCI=y
CONFIG_SATA_SVW=y
CONFIG_ATA_PIIX=y
@ -670,6 +677,7 @@ CONFIG_SATA_SIL=y
# CONFIG_SATA_ULI is not set
CONFIG_SATA_VIA=y
# CONFIG_SATA_VITESSE is not set
# CONFIG_SATA_INIC162X is not set
CONFIG_SATA_INTEL_COMBINED=y
# CONFIG_PATA_ALI is not set
# CONFIG_PATA_AMD is not set
@ -687,6 +695,7 @@ CONFIG_SATA_INTEL_COMBINED=y
# CONFIG_PATA_HPT3X2N is not set
# CONFIG_PATA_HPT3X3 is not set
# CONFIG_PATA_IT821X is not set
# CONFIG_PATA_IT8213 is not set
# CONFIG_PATA_JMICRON is not set
# CONFIG_PATA_TRIFLEX is not set
# CONFIG_PATA_MARVELL is not set
@ -739,9 +748,7 @@ CONFIG_IEEE1394=y
# Subsystem Options
#
# CONFIG_IEEE1394_VERBOSEDEBUG is not set
# CONFIG_IEEE1394_OUI_DB is not set
# CONFIG_IEEE1394_EXTRA_CONFIG_ROMS is not set
# CONFIG_IEEE1394_EXPORT_FULL_API is not set
#
# Device Drivers
@ -766,6 +773,11 @@ CONFIG_IEEE1394_RAWIO=y
#
# CONFIG_I2O is not set
#
# Macintosh device drivers
#
# CONFIG_MAC_EMUMOUSEBTN is not set
#
# Network device support
#
@ -833,6 +845,7 @@ CONFIG_8139TOO=y
# CONFIG_SUNDANCE is not set
# CONFIG_TLAN is not set
# CONFIG_VIA_RHINE is not set
# CONFIG_SC92031 is not set
#
# Ethernet (1000 Mbit)
@ -855,11 +868,13 @@ CONFIG_SKY2=y
CONFIG_TIGON3=y
CONFIG_BNX2=y
# CONFIG_QLA3XXX is not set
# CONFIG_ATL1 is not set
#
# Ethernet (10000 Mbit)
#
# CONFIG_CHELSIO_T1 is not set
# CONFIG_CHELSIO_T3 is not set
# CONFIG_IXGB is not set
# CONFIG_S2IO is not set
# CONFIG_MYRI10GE is not set
@ -1090,6 +1105,7 @@ CONFIG_SOUND=y
# Open Sound System
#
CONFIG_SOUND_PRIME=y
CONFIG_OBSOLETE_OSS=y
# CONFIG_SOUND_BT878 is not set
# CONFIG_SOUND_ES1371 is not set
CONFIG_SOUND_ICH=y
@ -1103,6 +1119,7 @@ CONFIG_SOUND_ICH=y
# HID Devices
#
CONFIG_HID=y
# CONFIG_HID_DEBUG is not set
#
# USB support
@ -1117,10 +1134,8 @@ CONFIG_USB=y
# Miscellaneous USB options
#
CONFIG_USB_DEVICEFS=y
# CONFIG_USB_BANDWIDTH is not set
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_SUSPEND is not set
# CONFIG_USB_MULTITHREAD_PROBE is not set
# CONFIG_USB_OTG is not set
#
@ -1130,9 +1145,11 @@ CONFIG_USB_EHCI_HCD=y
# CONFIG_USB_EHCI_SPLIT_ISO is not set
# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
# CONFIG_USB_EHCI_TT_NEWSCHED is not set
# CONFIG_USB_EHCI_BIG_ENDIAN_MMIO is not set
# CONFIG_USB_ISP116X_HCD is not set
CONFIG_USB_OHCI_HCD=y
# CONFIG_USB_OHCI_BIG_ENDIAN is not set
# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
CONFIG_USB_OHCI_LITTLE_ENDIAN=y
CONFIG_USB_UHCI_HCD=y
# CONFIG_USB_SL811_HCD is not set
@ -1183,6 +1200,7 @@ CONFIG_USB_HID=y
# CONFIG_USB_ATI_REMOTE2 is not set
# CONFIG_USB_KEYSPAN_REMOTE is not set
# CONFIG_USB_APPLETOUCH is not set
# CONFIG_USB_GTCO is not set
#
# USB Imaging devices
@ -1287,6 +1305,10 @@ CONFIG_USB_MON=y
# DMA Devices
#
#
# Auxiliary Display support
#
#
# Virtualization
#
@ -1480,6 +1502,7 @@ CONFIG_UNUSED_SYMBOLS=y
# CONFIG_DEBUG_FS is not set
# CONFIG_HEADERS_CHECK is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_SHIRQ is not set
CONFIG_LOG_BUF_SHIFT=18
CONFIG_DETECT_SOFTLOCKUP=y
# CONFIG_SCHEDSTATS is not set
@ -1488,7 +1511,6 @@ CONFIG_DETECT_SOFTLOCKUP=y
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_MUTEXES is not set
# CONFIG_DEBUG_RWSEMS is not set
# CONFIG_DEBUG_LOCK_ALLOC is not set
# CONFIG_PROVE_LOCKING is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
@ -1533,7 +1555,8 @@ CONFIG_CRC32=y
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_PLIST=y
CONFIG_IOMAP_COPY=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_GENERIC_HARDIRQS=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_GENERIC_PENDING_IRQ=y

View File

@ -18,7 +18,7 @@ obj-$(CONFIG_X86_MSR) += msr.o
obj-$(CONFIG_X86_CPUID) += cpuid.o
obj-$(CONFIG_MICROCODE) += microcode.o
obj-$(CONFIG_APM) += apm.o
obj-$(CONFIG_X86_SMP) += smp.o smpboot.o
obj-$(CONFIG_X86_SMP) += smp.o smpboot.o tsc_sync.o
obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
obj-$(CONFIG_X86_MPPARSE) += mpparse.o
obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o
@ -32,7 +32,6 @@ obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_MODULES) += module.o
obj-y += sysenter.o vsyscall.o
obj-$(CONFIG_ACPI_SRAT) += srat.o
obj-$(CONFIG_HPET_TIMER) += time_hpet.o
obj-$(CONFIG_EFI) += efi.o efi_stub.o
obj-$(CONFIG_DOUBLEFAULT) += doublefault.o
obj-$(CONFIG_VM86) += vm86.o
@ -40,8 +39,9 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_HPET_TIMER) += hpet.o
obj-$(CONFIG_K8_NB) += k8.o
# Make sure this is linked after any other paravirt_ops structs: see head.S
obj-$(CONFIG_VMI) += vmi.o vmitime.o
obj-$(CONFIG_PARAVIRT) += paravirt.o
obj-y += pcspeaker.o
EXTRA_AFLAGS := -traditional

View File

@ -25,6 +25,7 @@
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/acpi_pmtmr.h>
#include <linux/efi.h>
#include <linux/cpumask.h>
#include <linux/module.h>
@ -615,6 +616,7 @@ static int __init acpi_parse_sbf(struct acpi_table_header *table)
}
#ifdef CONFIG_HPET_TIMER
#include <asm/hpet.h>
static int __init acpi_parse_hpet(struct acpi_table_header *table)
{
@ -645,24 +647,11 @@ static int __init acpi_parse_hpet(struct acpi_table_header *table)
hpet_res->end = (1 * 1024) - 1;
}
#ifdef CONFIG_X86_64
vxtime.hpet_address = hpet_tbl->address.address;
hpet_address = hpet_tbl->address.address;
printk(KERN_INFO PREFIX "HPET id: %#x base: %#lx\n",
hpet_tbl->id, vxtime.hpet_address);
hpet_tbl->id, hpet_address);
res_start = vxtime.hpet_address;
#else /* X86 */
{
extern unsigned long hpet_address;
hpet_address = hpet_tbl->address.address;
printk(KERN_INFO PREFIX "HPET id: %#x base: %#lx\n",
hpet_tbl->id, hpet_address);
res_start = hpet_address;
}
#endif /* X86 */
res_start = hpet_address;
if (hpet_res) {
hpet_res->start = res_start;
@ -676,10 +665,6 @@ static int __init acpi_parse_hpet(struct acpi_table_header *table)
#define acpi_parse_hpet NULL
#endif
#ifdef CONFIG_X86_PM_TIMER
extern u32 pmtmr_ioport;
#endif
static int __init acpi_parse_fadt(struct acpi_table_header *table)
{

File diff suppressed because it is too large Load Diff

View File

@ -211,6 +211,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/miscdevice.h>
#include <linux/apm_bios.h>
#include <linux/init.h>
@ -235,7 +236,6 @@
#include "io_ports.h"
extern unsigned long get_cmos_time(void);
extern void machine_real_restart(unsigned char *, int);
#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
@ -1175,28 +1175,6 @@ static void queue_event(apm_event_t event, struct apm_user *sender)
spin_unlock(&user_list_lock);
}
static void set_time(void)
{
struct timespec ts;
if (got_clock_diff) { /* Must know time zone in order to set clock */
ts.tv_sec = get_cmos_time() + clock_cmos_diff;
ts.tv_nsec = 0;
do_settimeofday(&ts);
}
}
static void get_time_diff(void)
{
#ifndef CONFIG_APM_RTC_IS_GMT
/*
* Estimate time zone so that set_time can update the clock
*/
clock_cmos_diff = -get_cmos_time();
clock_cmos_diff += get_seconds();
got_clock_diff = 1;
#endif
}
static void reinit_timer(void)
{
#ifdef INIT_TIMER_AFTER_SUSPEND
@ -1236,19 +1214,6 @@ static int suspend(int vetoable)
local_irq_disable();
device_power_down(PMSG_SUSPEND);
/* serialize with the timer interrupt */
write_seqlock(&xtime_lock);
/* protect against access to timer chip registers */
spin_lock(&i8253_lock);
get_time_diff();
/*
* Irq spinlock must be dropped around set_system_power_state.
* We'll undo any timer changes due to interrupts below.
*/
spin_unlock(&i8253_lock);
write_sequnlock(&xtime_lock);
local_irq_enable();
save_processor_state();
@ -1257,7 +1222,6 @@ static int suspend(int vetoable)
restore_processor_state();
local_irq_disable();
set_time();
reinit_timer();
if (err == APM_NO_ERROR)
@ -1287,11 +1251,6 @@ static void standby(void)
local_irq_disable();
device_power_down(PMSG_SUSPEND);
/* serialize with the timer interrupt */
write_seqlock(&xtime_lock);
/* If needed, notify drivers here */
get_time_diff();
write_sequnlock(&xtime_lock);
local_irq_enable();
err = set_system_power_state(APM_STATE_STANDBY);
@ -1385,7 +1344,6 @@ static void check_events(void)
ignore_bounce = 1;
if ((event != APM_NORMAL_RESUME)
|| (ignore_normal_resume == 0)) {
set_time();
device_resume();
pm_send_all(PM_RESUME, (void *)0);
queue_event(event, NULL);
@ -1401,7 +1359,6 @@ static void check_events(void)
break;
case APM_UPDATE_TIME:
set_time();
break;
case APM_CRITICAL_SUSPEND:
@ -1636,9 +1593,8 @@ static int do_open(struct inode * inode, struct file * filp)
return 0;
}
static int apm_get_info(char *buf, char **start, off_t fpos, int length)
static int proc_apm_show(struct seq_file *m, void *v)
{
char * p;
unsigned short bx;
unsigned short cx;
unsigned short dx;
@ -1650,8 +1606,6 @@ static int apm_get_info(char *buf, char **start, off_t fpos, int length)
int time_units = -1;
char *units = "?";
p = buf;
if ((num_online_cpus() == 1) &&
!(error = apm_get_power_status(&bx, &cx, &dx))) {
ac_line_status = (bx >> 8) & 0xff;
@ -1705,7 +1659,7 @@ static int apm_get_info(char *buf, char **start, off_t fpos, int length)
-1: Unknown
8) min = minutes; sec = seconds */
p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
seq_printf(m, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
driver_version,
(apm_info.bios.version >> 8) & 0xff,
apm_info.bios.version & 0xff,
@ -1716,10 +1670,22 @@ static int apm_get_info(char *buf, char **start, off_t fpos, int length)
percentage,
time_units,
units);
return p - buf;
return 0;
}
static int proc_apm_open(struct inode *inode, struct file *file)
{
return single_open(file, proc_apm_show, NULL);
}
static const struct file_operations apm_file_ops = {
.owner = THIS_MODULE,
.open = proc_apm_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int apm(void *unused)
{
unsigned short bx;
@ -2341,9 +2307,9 @@ static int __init apm_init(void)
set_base(gdt[APM_DS >> 3],
__va((unsigned long)apm_info.bios.dseg << 4));
apm_proc = create_proc_info_entry("apm", 0, NULL, apm_get_info);
apm_proc = create_proc_entry("apm", 0, NULL);
if (apm_proc)
apm_proc->owner = THIS_MODULE;
apm_proc->proc_fops = &apm_file_ops;
kapmd_task = kthread_create(apm, NULL, "kapmd");
if (IS_ERR(kapmd_task)) {

View File

@ -72,7 +72,7 @@ void foo(void)
OFFSET(PT_EAX, pt_regs, eax);
OFFSET(PT_DS, pt_regs, xds);
OFFSET(PT_ES, pt_regs, xes);
OFFSET(PT_GS, pt_regs, xgs);
OFFSET(PT_FS, pt_regs, xfs);
OFFSET(PT_ORIG_EAX, pt_regs, orig_eax);
OFFSET(PT_EIP, pt_regs, eip);
OFFSET(PT_CS, pt_regs, xcs);

View File

@ -605,7 +605,7 @@ void __init early_cpu_init(void)
struct pt_regs * __devinit idle_regs(struct pt_regs *regs)
{
memset(regs, 0, sizeof(struct pt_regs));
regs->xgs = __KERNEL_PDA;
regs->xfs = __KERNEL_PDA;
return regs;
}
@ -662,12 +662,12 @@ struct i386_pda boot_pda = {
.pcurrent = &init_task,
};
static inline void set_kernel_gs(void)
static inline void set_kernel_fs(void)
{
/* Set %gs for this CPU's PDA. Memory clobber is to create a
/* Set %fs for this CPU's PDA. Memory clobber is to create a
barrier with respect to any PDA operations, so the compiler
doesn't move any before here. */
asm volatile ("mov %0, %%gs" : : "r" (__KERNEL_PDA) : "memory");
asm volatile ("mov %0, %%fs" : : "r" (__KERNEL_PDA) : "memory");
}
/* Initialize the CPU's GDT and PDA. The boot CPU does this for
@ -718,7 +718,7 @@ void __cpuinit cpu_set_gdt(int cpu)
the boot CPU, this will transition from the boot gdt+pda to
the real ones). */
load_gdt(cpu_gdt_descr);
set_kernel_gs();
set_kernel_fs();
}
/* Common CPU init for both boot and secondary CPUs */
@ -764,8 +764,8 @@ static void __cpuinit _cpu_init(int cpu, struct task_struct *curr)
__set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss);
#endif
/* Clear %fs. */
asm volatile ("mov %0, %%fs" : : "r" (0));
/* Clear %gs. */
asm volatile ("mov %0, %%gs" : : "r" (0));
/* Clear all 6 debug registers: */
set_debugreg(0, 0);

View File

@ -217,6 +217,15 @@ config X86_LONGHAUL
If in doubt, say N.
config X86_E_POWERSAVER
tristate "VIA C7 Enhanced PowerSaver (EXPERIMENTAL)"
select CPU_FREQ_TABLE
depends on EXPERIMENTAL
help
This adds the CPUFreq driver for VIA C7 processors.
If in doubt, say N.
comment "shared options"
config X86_ACPI_CPUFREQ_PROC_INTF

View File

@ -2,6 +2,7 @@ obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o
obj-$(CONFIG_X86_POWERNOW_K7) += powernow-k7.o
obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o
obj-$(CONFIG_X86_LONGHAUL) += longhaul.o
obj-$(CONFIG_X86_E_POWERSAVER) += e_powersaver.o
obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o
obj-$(CONFIG_SC520_CPUFREQ) += sc520_freq.o
obj-$(CONFIG_X86_LONGRUN) += longrun.o

View File

@ -0,0 +1,334 @@
/*
* Based on documentation provided by Dave Jones. Thanks!
*
* Licensed under the terms of the GNU GPL License version 2.
*
* BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <asm/msr.h>
#include <asm/tsc.h>
#include <asm/timex.h>
#include <asm/io.h>
#include <asm/delay.h>
#define EPS_BRAND_C7M 0
#define EPS_BRAND_C7 1
#define EPS_BRAND_EDEN 2
#define EPS_BRAND_C3 3
struct eps_cpu_data {
u32 fsb;
struct cpufreq_frequency_table freq_table[];
};
static struct eps_cpu_data *eps_cpu[NR_CPUS];
static unsigned int eps_get(unsigned int cpu)
{
struct eps_cpu_data *centaur;
u32 lo, hi;
if (cpu)
return 0;
centaur = eps_cpu[cpu];
if (centaur == NULL)
return 0;
/* Return current frequency */
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
return centaur->fsb * ((lo >> 8) & 0xff);
}
static int eps_set_state(struct eps_cpu_data *centaur,
unsigned int cpu,
u32 dest_state)
{
struct cpufreq_freqs freqs;
u32 lo, hi;
int err = 0;
int i;
freqs.old = eps_get(cpu);
freqs.new = centaur->fsb * ((dest_state >> 8) & 0xff);
freqs.cpu = cpu;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
/* Wait while CPU is busy */
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
i = 0;
while (lo & ((1 << 16) | (1 << 17))) {
udelay(16);
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
i++;
if (unlikely(i > 64)) {
err = -ENODEV;
goto postchange;
}
}
/* Set new multiplier and voltage */
wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0);
/* Wait until transition end */
i = 0;
do {
udelay(16);
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
i++;
if (unlikely(i > 64)) {
err = -ENODEV;
goto postchange;
}
} while (lo & ((1 << 16) | (1 << 17)));
/* Return current frequency */
postchange:
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
freqs.new = centaur->fsb * ((lo >> 8) & 0xff);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return err;
}
static int eps_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
struct eps_cpu_data *centaur;
unsigned int newstate = 0;
unsigned int cpu = policy->cpu;
unsigned int dest_state;
int ret;
if (unlikely(eps_cpu[cpu] == NULL))
return -ENODEV;
centaur = eps_cpu[cpu];
if (unlikely(cpufreq_frequency_table_target(policy,
&eps_cpu[cpu]->freq_table[0],
target_freq,
relation,
&newstate))) {
return -EINVAL;
}
/* Make frequency transition */
dest_state = centaur->freq_table[newstate].index & 0xffff;
ret = eps_set_state(centaur, cpu, dest_state);
if (ret)
printk(KERN_ERR "eps: Timeout!\n");
return ret;
}
static int eps_verify(struct cpufreq_policy *policy)
{
return cpufreq_frequency_table_verify(policy,
&eps_cpu[policy->cpu]->freq_table[0]);
}
static int eps_cpu_init(struct cpufreq_policy *policy)
{
unsigned int i;
u32 lo, hi;
u64 val;
u8 current_multiplier, current_voltage;
u8 max_multiplier, max_voltage;
u8 min_multiplier, min_voltage;
u8 brand;
u32 fsb;
struct eps_cpu_data *centaur;
struct cpufreq_frequency_table *f_table;
int k, step, voltage;
int ret;
int states;
if (policy->cpu != 0)
return -ENODEV;
/* Check brand */
printk("eps: Detected VIA ");
rdmsr(0x1153, lo, hi);
brand = (((lo >> 2) ^ lo) >> 18) & 3;
switch(brand) {
case EPS_BRAND_C7M:
printk("C7-M\n");
break;
case EPS_BRAND_C7:
printk("C7\n");
break;
case EPS_BRAND_EDEN:
printk("Eden\n");
break;
case EPS_BRAND_C3:
printk("C3\n");
return -ENODEV;
break;
}
/* Enable Enhanced PowerSaver */
rdmsrl(MSR_IA32_MISC_ENABLE, val);
if (!(val & 1 << 16)) {
val |= 1 << 16;
wrmsrl(MSR_IA32_MISC_ENABLE, val);
/* Can be locked at 0 */
rdmsrl(MSR_IA32_MISC_ENABLE, val);
if (!(val & 1 << 16)) {
printk("eps: Can't enable Enhanced PowerSaver\n");
return -ENODEV;
}
}
/* Print voltage and multiplier */
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
current_voltage = lo & 0xff;
printk("eps: Current voltage = %dmV\n", current_voltage * 16 + 700);
current_multiplier = (lo >> 8) & 0xff;
printk("eps: Current multiplier = %d\n", current_multiplier);
/* Print limits */
max_voltage = hi & 0xff;
printk("eps: Highest voltage = %dmV\n", max_voltage * 16 + 700);
max_multiplier = (hi >> 8) & 0xff;
printk("eps: Highest multiplier = %d\n", max_multiplier);
min_voltage = (hi >> 16) & 0xff;
printk("eps: Lowest voltage = %dmV\n", min_voltage * 16 + 700);
min_multiplier = (hi >> 24) & 0xff;
printk("eps: Lowest multiplier = %d\n", min_multiplier);
/* Sanity checks */
if (current_multiplier == 0 || max_multiplier == 0
|| min_multiplier == 0)
return -EINVAL;
if (current_multiplier > max_multiplier
|| max_multiplier <= min_multiplier)
return -EINVAL;
if (current_voltage > 0x1c || max_voltage > 0x1c)
return -EINVAL;
if (max_voltage < min_voltage)
return -EINVAL;
/* Calc FSB speed */
fsb = cpu_khz / current_multiplier;
/* Calc number of p-states supported */
if (brand == EPS_BRAND_C7M)
states = max_multiplier - min_multiplier + 1;
else
states = 2;
/* Allocate private data and frequency table for current cpu */
centaur = kzalloc(sizeof(struct eps_cpu_data)
+ (states + 1) * sizeof(struct cpufreq_frequency_table),
GFP_KERNEL);
if (!centaur)
return -ENOMEM;
eps_cpu[0] = centaur;
/* Copy basic values */
centaur->fsb = fsb;
/* Fill frequency and MSR value table */
f_table = &centaur->freq_table[0];
if (brand != EPS_BRAND_C7M) {
f_table[0].frequency = fsb * min_multiplier;
f_table[0].index = (min_multiplier << 8) | min_voltage;
f_table[1].frequency = fsb * max_multiplier;
f_table[1].index = (max_multiplier << 8) | max_voltage;
f_table[2].frequency = CPUFREQ_TABLE_END;
} else {
k = 0;
step = ((max_voltage - min_voltage) * 256)
/ (max_multiplier - min_multiplier);
for (i = min_multiplier; i <= max_multiplier; i++) {
voltage = (k * step) / 256 + min_voltage;
f_table[k].frequency = fsb * i;
f_table[k].index = (i << 8) | voltage;
k++;
}
f_table[k].frequency = CPUFREQ_TABLE_END;
}
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */
policy->cur = fsb * current_multiplier;
ret = cpufreq_frequency_table_cpuinfo(policy, &centaur->freq_table[0]);
if (ret) {
kfree(centaur);
return ret;
}
cpufreq_frequency_table_get_attr(&centaur->freq_table[0], policy->cpu);
return 0;
}
static int eps_cpu_exit(struct cpufreq_policy *policy)
{
unsigned int cpu = policy->cpu;
struct eps_cpu_data *centaur;
u32 lo, hi;
if (eps_cpu[cpu] == NULL)
return -ENODEV;
centaur = eps_cpu[cpu];
/* Get max frequency */
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
/* Set max frequency */
eps_set_state(centaur, cpu, hi & 0xffff);
/* Bye */
cpufreq_frequency_table_put_attr(policy->cpu);
kfree(eps_cpu[cpu]);
eps_cpu[cpu] = NULL;
return 0;
}
static struct freq_attr* eps_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
static struct cpufreq_driver eps_driver = {
.verify = eps_verify,
.target = eps_target,
.init = eps_cpu_init,
.exit = eps_cpu_exit,
.get = eps_get,
.name = "e_powersaver",
.owner = THIS_MODULE,
.attr = eps_attr,
};
static int __init eps_init(void)
{
struct cpuinfo_x86 *c = cpu_data;
/* This driver will work only on Centaur C7 processors with
* Enhanced SpeedStep/PowerSaver registers */
if (c->x86_vendor != X86_VENDOR_CENTAUR
|| c->x86 != 6 || c->x86_model != 10)
return -ENODEV;
if (!cpu_has(c, X86_FEATURE_EST))
return -ENODEV;
if (cpufreq_register_driver(&eps_driver))
return -EINVAL;
return 0;
}
static void __exit eps_exit(void)
{
cpufreq_unregister_driver(&eps_driver);
}
MODULE_AUTHOR("Rafa³ Bilski <rafalbilski@interia.pl>");
MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
MODULE_LICENSE("GPL");
module_init(eps_init);
module_exit(eps_exit);

View File

@ -8,12 +8,11 @@
* VIA have currently 3 different versions of Longhaul.
* Version 1 (Longhaul) uses the BCR2 MSR at 0x1147.
* It is present only in Samuel 1 (C5A), Samuel 2 (C5B) stepping 0.
* Version 2 of longhaul is the same as v1, but adds voltage scaling.
* Present in Samuel 2 (steppings 1-7 only) (C5B), and Ezra (C5C)
* voltage scaling support has currently been disabled in this driver
* until we have code that gets it right.
* Version 2 of longhaul is backward compatible with v1, but adds
* LONGHAUL MSR for purpose of both frequency and voltage scaling.
* Present in Samuel 2 (steppings 1-7 only) (C5B), and Ezra (C5C).
* Version 3 of longhaul got renamed to Powersaver and redesigned
* to use the POWERSAVER MSR at 0x110a.
* to use only the POWERSAVER MSR at 0x110a.
* It is present in Ezra-T (C5M), Nehemiah (C5X) and above.
* It's pretty much the same feature wise to longhaul v2, though
* there is provision for scaling FSB too, but this doesn't work
@ -51,10 +50,12 @@
#define CPU_EZRA 3
#define CPU_EZRA_T 4
#define CPU_NEHEMIAH 5
#define CPU_NEHEMIAH_C 6
/* Flags */
#define USE_ACPI_C3 (1 << 1)
#define USE_NORTHBRIDGE (1 << 2)
#define USE_VT8235 (1 << 3)
static int cpu_model;
static unsigned int numscales=16;
@ -63,7 +64,8 @@ static unsigned int fsb;
static struct mV_pos *vrm_mV_table;
static unsigned char *mV_vrm_table;
struct f_msr {
unsigned char vrm;
u8 vrm;
u8 pos;
};
static struct f_msr f_msr_table[32];
@ -73,10 +75,10 @@ static int can_scale_voltage;
static struct acpi_processor *pr = NULL;
static struct acpi_processor_cx *cx = NULL;
static u8 longhaul_flags;
static u8 longhaul_pos;
/* Module parameters */
static int scale_voltage;
static int ignore_latency;
#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg)
@ -164,26 +166,47 @@ static void do_longhaul1(unsigned int clock_ratio_index)
static void do_powersaver(int cx_address, unsigned int clock_ratio_index)
{
union msr_longhaul longhaul;
u8 dest_pos;
u32 t;
dest_pos = f_msr_table[clock_ratio_index].pos;
rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
/* Setup new frequency */
longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
longhaul.bits.EnableSoftBusRatio = 1;
if (can_scale_voltage) {
/* Setup new voltage */
if (can_scale_voltage)
longhaul.bits.SoftVID = f_msr_table[clock_ratio_index].vrm;
longhaul.bits.EnableSoftVID = 1;
}
/* Sync to timer tick */
safe_halt();
/* Raise voltage if necessary */
if (can_scale_voltage && longhaul_pos < dest_pos) {
longhaul.bits.EnableSoftVID = 1;
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
/* Change voltage */
if (!cx_address) {
ACPI_FLUSH_CPU_CACHE();
halt();
} else {
ACPI_FLUSH_CPU_CACHE();
/* Invoke C3 */
inb(cx_address);
/* Dummy op - must do something useless after P_LVL3
* read */
t = inl(acpi_gbl_FADT.xpm_timer_block.address);
}
longhaul.bits.EnableSoftVID = 0;
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
longhaul_pos = dest_pos;
}
/* Change frequency on next halt or sleep */
longhaul.bits.EnableSoftBusRatio = 1;
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
if (!cx_address) {
ACPI_FLUSH_CPU_CACHE();
/* Invoke C1 */
halt();
} else {
ACPI_FLUSH_CPU_CACHE();
@ -193,12 +216,29 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index)
t = inl(acpi_gbl_FADT.xpm_timer_block.address);
}
/* Disable bus ratio bit */
local_irq_disable();
longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
longhaul.bits.EnableSoftBusRatio = 0;
longhaul.bits.EnableSoftBSEL = 0;
longhaul.bits.EnableSoftVID = 0;
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
/* Reduce voltage if necessary */
if (can_scale_voltage && longhaul_pos > dest_pos) {
longhaul.bits.EnableSoftVID = 1;
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
/* Change voltage */
if (!cx_address) {
ACPI_FLUSH_CPU_CACHE();
halt();
} else {
ACPI_FLUSH_CPU_CACHE();
/* Invoke C3 */
inb(cx_address);
/* Dummy op - must do something useless after P_LVL3
* read */
t = inl(acpi_gbl_FADT.xpm_timer_block.address);
}
longhaul.bits.EnableSoftVID = 0;
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
longhaul_pos = dest_pos;
}
}
/**
@ -257,26 +297,19 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
/*
* Longhaul v1. (Samuel[C5A] and Samuel2 stepping 0[C5B])
* Software controlled multipliers only.
*
* *NB* Until we get voltage scaling working v1 & v2 are the same code.
* Longhaul v2 appears in Samuel2 Steppings 1->7 [C5b] and Ezra [C5C]
*/
case TYPE_LONGHAUL_V1:
case TYPE_LONGHAUL_V2:
do_longhaul1(clock_ratio_index);
break;
/*
* Longhaul v2 appears in Samuel2 Steppings 1->7 [C5B] and Ezra [C5C]
*
* Longhaul v3 (aka Powersaver). (Ezra-T [C5M] & Nehemiah [C5N])
* We can scale voltage with this too, but that's currently
* disabled until we come up with a decent 'match freq to voltage'
* algorithm.
* When we add voltage scaling, we will also need to do the
* voltage/freq setting in order depending on the direction
* of scaling (like we do in powernow-k7.c)
* Nehemiah can do FSB scaling too, but this has never been proven
* to work in practice.
*/
case TYPE_LONGHAUL_V2:
case TYPE_POWERSAVER:
if (longhaul_flags & USE_ACPI_C3) {
/* Don't allow wakeup */
@ -301,6 +334,7 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
local_irq_restore(flags);
preempt_enable();
freqs.new = calc_speed(longhaul_get_cpu_mult());
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}
@ -315,31 +349,19 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
#define ROUNDING 0xf
static int _guess(int guess, int mult)
{
int target;
target = ((mult/10)*guess);
if (mult%10 != 0)
target += (guess/2);
target += ROUNDING/2;
target &= ~ROUNDING;
return target;
}
static int guess_fsb(int mult)
{
int speed = (cpu_khz/1000);
int speed = cpu_khz / 1000;
int i;
int speeds[] = { 66, 100, 133, 200 };
int speeds[] = { 666, 1000, 1333, 2000 };
int f_max, f_min;
speed += ROUNDING/2;
speed &= ~ROUNDING;
for (i=0; i<4; i++) {
if (_guess(speeds[i], mult) == speed)
return speeds[i];
for (i = 0; i < 4; i++) {
f_max = ((speeds[i] * mult) + 50) / 100;
f_max += (ROUNDING / 2);
f_min = f_max - ROUNDING;
if ((speed <= f_max) && (speed >= f_min))
return speeds[i] / 10;
}
return 0;
}
@ -347,67 +369,40 @@ static int guess_fsb(int mult)
static int __init longhaul_get_ranges(void)
{
unsigned long invalue;
unsigned int ezra_t_multipliers[32]= {
90, 30, 40, 100, 55, 35, 45, 95,
50, 70, 80, 60, 120, 75, 85, 65,
-1, 110, 120, -1, 135, 115, 125, 105,
130, 150, 160, 140, -1, 155, -1, 145 };
unsigned int j, k = 0;
union msr_longhaul longhaul;
int mult = 0;
int mult;
switch (longhaul_version) {
case TYPE_LONGHAUL_V1:
case TYPE_LONGHAUL_V2:
/* Ugh, Longhaul v1 didn't have the min/max MSRs.
Assume min=3.0x & max = whatever we booted at. */
minmult = 30;
maxmult = mult = longhaul_get_cpu_mult();
break;
case TYPE_POWERSAVER:
/* Ezra-T */
if (cpu_model==CPU_EZRA_T) {
minmult = 30;
rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
invalue = longhaul.bits.MaxMHzBR;
if (longhaul.bits.MaxMHzBR4)
invalue += 16;
maxmult = mult = ezra_t_multipliers[invalue];
break;
}
/* Nehemiah */
if (cpu_model==CPU_NEHEMIAH) {
rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
/*
* TODO: This code works, but raises a lot of questions.
* - Some Nehemiah's seem to have broken Min/MaxMHzBR's.
* We get around this by using a hardcoded multiplier of 4.0x
* for the minimimum speed, and the speed we booted up at for the max.
* This is done in longhaul_get_cpu_mult() by reading the EBLCR register.
* - According to some VIA documentation EBLCR is only
* in pre-Nehemiah C3s. How this still works is a mystery.
* We're possibly using something undocumented and unsupported,
* But it works, so we don't grumble.
*/
minmult=40;
maxmult = mult = longhaul_get_cpu_mult();
break;
}
/* Get current frequency */
mult = longhaul_get_cpu_mult();
if (mult == -1) {
printk(KERN_INFO PFX "Invalid (reserved) multiplier!\n");
return -EINVAL;
}
fsb = guess_fsb(mult);
if (fsb == 0) {
printk(KERN_INFO PFX "Invalid (reserved) FSB!\n");
return -EINVAL;
}
/* Get max multiplier - as we always did.
* Longhaul MSR is usefull only when voltage scaling is enabled.
* C3 is booting at max anyway. */
maxmult = mult;
/* Get min multiplier */
switch (cpu_model) {
case CPU_NEHEMIAH:
minmult = 50;
break;
case CPU_NEHEMIAH_C:
minmult = 40;
break;
default:
minmult = 30;
break;
}
dprintk ("MinMult:%d.%dx MaxMult:%d.%dx\n",
minmult/10, minmult%10, maxmult/10, maxmult%10);
if (fsb == 0) {
printk (KERN_INFO PFX "Invalid (reserved) FSB!\n");
return -EINVAL;
}
highest_speed = calc_speed(maxmult);
lowest_speed = calc_speed(minmult);
dprintk ("FSB:%dMHz Lowest speed: %s Highest speed:%s\n", fsb,
@ -455,6 +450,7 @@ static void __init longhaul_setup_voltagescaling(void)
union msr_longhaul longhaul;
struct mV_pos minvid, maxvid;
unsigned int j, speed, pos, kHz_step, numvscales;
int min_vid_speed;
rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
if (!(longhaul.bits.RevisionID & 1)) {
@ -468,14 +464,14 @@ static void __init longhaul_setup_voltagescaling(void)
mV_vrm_table = &mV_vrm85[0];
} else {
printk (KERN_INFO PFX "Mobile VRM\n");
if (cpu_model < CPU_NEHEMIAH)
return;
vrm_mV_table = &mobilevrm_mV[0];
mV_vrm_table = &mV_mobilevrm[0];
}
minvid = vrm_mV_table[longhaul.bits.MinimumVID];
maxvid = vrm_mV_table[longhaul.bits.MaximumVID];
numvscales = maxvid.pos - minvid.pos + 1;
kHz_step = (highest_speed - lowest_speed) / numvscales;
if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) {
printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
@ -491,20 +487,59 @@ static void __init longhaul_setup_voltagescaling(void)
return;
}
printk(KERN_INFO PFX "Max VID=%d.%03d Min VID=%d.%03d, %d possible voltage scales\n",
/* How many voltage steps */
numvscales = maxvid.pos - minvid.pos + 1;
printk(KERN_INFO PFX
"Max VID=%d.%03d "
"Min VID=%d.%03d, "
"%d possible voltage scales\n",
maxvid.mV/1000, maxvid.mV%1000,
minvid.mV/1000, minvid.mV%1000,
numvscales);
/* Calculate max frequency at min voltage */
j = longhaul.bits.MinMHzBR;
if (longhaul.bits.MinMHzBR4)
j += 16;
min_vid_speed = eblcr_table[j];
if (min_vid_speed == -1)
return;
switch (longhaul.bits.MinMHzFSB) {
case 0:
min_vid_speed *= 13333;
break;
case 1:
min_vid_speed *= 10000;
break;
case 3:
min_vid_speed *= 6666;
break;
default:
return;
break;
}
if (min_vid_speed >= highest_speed)
return;
/* Calculate kHz for one voltage step */
kHz_step = (highest_speed - min_vid_speed) / numvscales;
j = 0;
while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) {
speed = longhaul_table[j].frequency;
pos = (speed - lowest_speed) / kHz_step + minvid.pos;
if (speed > min_vid_speed)
pos = (speed - min_vid_speed) / kHz_step + minvid.pos;
else
pos = minvid.pos;
f_msr_table[longhaul_table[j].index].vrm = mV_vrm_table[pos];
f_msr_table[longhaul_table[j].index].pos = pos;
j++;
}
longhaul_pos = maxvid.pos;
can_scale_voltage = 1;
printk(KERN_INFO PFX "Voltage scaling enabled. "
"Use of \"conservative\" governor is highly recommended.\n");
}
@ -573,20 +608,51 @@ static int enable_arbiter_disable(void)
if (dev != NULL) {
/* Enable access to port 0x22 */
pci_read_config_byte(dev, reg, &pci_cmd);
if ( !(pci_cmd & 1<<7) ) {
if (!(pci_cmd & 1<<7)) {
pci_cmd |= 1<<7;
pci_write_config_byte(dev, reg, pci_cmd);
pci_read_config_byte(dev, reg, &pci_cmd);
if (!(pci_cmd & 1<<7)) {
printk(KERN_ERR PFX
"Can't enable access to port 0x22.\n");
return 0;
}
}
return 1;
}
return 0;
}
static int longhaul_setup_vt8235(void)
{
struct pci_dev *dev;
u8 pci_cmd;
/* Find VT8235 southbridge */
dev = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL);
if (dev != NULL) {
/* Set transition time to max */
pci_read_config_byte(dev, 0xec, &pci_cmd);
pci_cmd &= ~(1 << 2);
pci_write_config_byte(dev, 0xec, pci_cmd);
pci_read_config_byte(dev, 0xe4, &pci_cmd);
pci_cmd &= ~(1 << 7);
pci_write_config_byte(dev, 0xe4, pci_cmd);
pci_read_config_byte(dev, 0xe5, &pci_cmd);
pci_cmd |= 1 << 7;
pci_write_config_byte(dev, 0xe5, pci_cmd);
return 1;
}
return 0;
}
static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
{
struct cpuinfo_x86 *c = cpu_data;
char *cpuname=NULL;
int ret;
u32 lo, hi;
int vt8235_present;
/* Check what we have on this motherboard */
switch (c->x86_model) {
@ -599,16 +665,20 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
break;
case 7:
longhaul_version = TYPE_LONGHAUL_V1;
switch (c->x86_mask) {
case 0:
longhaul_version = TYPE_LONGHAUL_V1;
cpu_model = CPU_SAMUEL2;
cpuname = "C3 'Samuel 2' [C5B]";
/* Note, this is not a typo, early Samuel2's had Samuel1 ratios. */
memcpy (clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio));
memcpy (eblcr_table, samuel2_eblcr, sizeof(samuel2_eblcr));
/* Note, this is not a typo, early Samuel2's had
* Samuel1 ratios. */
memcpy(clock_ratio, samuel1_clock_ratio,
sizeof(samuel1_clock_ratio));
memcpy(eblcr_table, samuel2_eblcr,
sizeof(samuel2_eblcr));
break;
case 1 ... 15:
longhaul_version = TYPE_LONGHAUL_V2;
if (c->x86_mask < 8) {
cpu_model = CPU_SAMUEL2;
cpuname = "C3 'Samuel 2' [C5B]";
@ -616,8 +686,10 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
cpu_model = CPU_EZRA;
cpuname = "C3 'Ezra' [C5C]";
}
memcpy (clock_ratio, ezra_clock_ratio, sizeof(ezra_clock_ratio));
memcpy (eblcr_table, ezra_eblcr, sizeof(ezra_eblcr));
memcpy(clock_ratio, ezra_clock_ratio,
sizeof(ezra_clock_ratio));
memcpy(eblcr_table, ezra_eblcr,
sizeof(ezra_eblcr));
break;
}
break;
@ -632,24 +704,24 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
break;
case 9:
cpu_model = CPU_NEHEMIAH;
longhaul_version = TYPE_POWERSAVER;
numscales=32;
numscales = 32;
memcpy(clock_ratio,
nehemiah_clock_ratio,
sizeof(nehemiah_clock_ratio));
memcpy(eblcr_table, nehemiah_eblcr, sizeof(nehemiah_eblcr));
switch (c->x86_mask) {
case 0 ... 1:
cpuname = "C3 'Nehemiah A' [C5N]";
memcpy (clock_ratio, nehemiah_a_clock_ratio, sizeof(nehemiah_a_clock_ratio));
memcpy (eblcr_table, nehemiah_a_eblcr, sizeof(nehemiah_a_eblcr));
cpu_model = CPU_NEHEMIAH;
cpuname = "C3 'Nehemiah A' [C5XLOE]";
break;
case 2 ... 4:
cpuname = "C3 'Nehemiah B' [C5N]";
memcpy (clock_ratio, nehemiah_b_clock_ratio, sizeof(nehemiah_b_clock_ratio));
memcpy (eblcr_table, nehemiah_b_eblcr, sizeof(nehemiah_b_eblcr));
cpu_model = CPU_NEHEMIAH;
cpuname = "C3 'Nehemiah B' [C5XLOH]";
break;
case 5 ... 15:
cpuname = "C3 'Nehemiah C' [C5N]";
memcpy (clock_ratio, nehemiah_c_clock_ratio, sizeof(nehemiah_c_clock_ratio));
memcpy (eblcr_table, nehemiah_c_eblcr, sizeof(nehemiah_c_eblcr));
cpu_model = CPU_NEHEMIAH_C;
cpuname = "C3 'Nehemiah C' [C5P]";
break;
}
break;
@ -658,6 +730,13 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
cpuname = "Unknown";
break;
}
/* Check Longhaul ver. 2 */
if (longhaul_version == TYPE_LONGHAUL_V2) {
rdmsr(MSR_VIA_LONGHAUL, lo, hi);
if (lo == 0 && hi == 0)
/* Looks like MSR isn't present */
longhaul_version = TYPE_LONGHAUL_V1;
}
printk (KERN_INFO PFX "VIA %s CPU detected. ", cpuname);
switch (longhaul_version) {
@ -670,15 +749,18 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
break;
};
/* Doesn't hurt */
vt8235_present = longhaul_setup_vt8235();
/* Find ACPI data for processor */
acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
&longhaul_walk_callback, NULL, (void *)&pr);
acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, &longhaul_walk_callback,
NULL, (void *)&pr);
/* Check ACPI support for C3 state */
if ((pr != NULL) && (longhaul_version == TYPE_POWERSAVER)) {
if (pr != NULL && longhaul_version != TYPE_LONGHAUL_V1) {
cx = &pr->power.states[ACPI_STATE_C3];
if (cx->address > 0 &&
(cx->latency <= 1000 || ignore_latency != 0) ) {
if (cx->address > 0 && cx->latency <= 1000) {
longhaul_flags |= USE_ACPI_C3;
goto print_support_type;
}
@ -688,8 +770,11 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
longhaul_flags |= USE_NORTHBRIDGE;
goto print_support_type;
}
/* No ACPI C3 or we can't use it */
/* Use VT8235 southbridge if present */
if (longhaul_version == TYPE_POWERSAVER && vt8235_present) {
longhaul_flags |= USE_VT8235;
goto print_support_type;
}
/* Check ACPI support for bus master arbiter disable */
if ((pr == NULL) || !(pr->flags.bm_control)) {
printk(KERN_ERR PFX
@ -698,18 +783,18 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
}
print_support_type:
if (!(longhaul_flags & USE_NORTHBRIDGE)) {
printk (KERN_INFO PFX "Using ACPI support.\n");
} else {
if (longhaul_flags & USE_NORTHBRIDGE)
printk (KERN_INFO PFX "Using northbridge support.\n");
}
else if (longhaul_flags & USE_VT8235)
printk (KERN_INFO PFX "Using VT8235 support.\n");
else
printk (KERN_INFO PFX "Using ACPI support.\n");
ret = longhaul_get_ranges();
if (ret != 0)
return ret;
if ((longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) &&
(scale_voltage != 0))
if ((longhaul_version != TYPE_LONGHAUL_V1) && (scale_voltage != 0))
longhaul_setup_voltagescaling();
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
@ -797,8 +882,6 @@ static void __exit longhaul_exit(void)
module_param (scale_voltage, int, 0644);
MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
module_param(ignore_latency, int, 0644);
MODULE_PARM_DESC(ignore_latency, "Skip ACPI C3 latency test");
MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");
MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");

View File

@ -235,49 +235,14 @@ static int __initdata ezrat_eblcr[32] = {
/*
* VIA C3 Nehemiah */
static int __initdata nehemiah_a_clock_ratio[32] = {
static int __initdata nehemiah_clock_ratio[32] = {
100, /* 0000 -> 10.0x */
160, /* 0001 -> 16.0x */
-1, /* 0010 -> RESERVED */
40, /* 0010 -> 4.0x */
90, /* 0011 -> 9.0x */
95, /* 0100 -> 9.5x */
-1, /* 0101 -> RESERVED */
-1, /* 0110 -> RESERVED */
55, /* 0111 -> 5.5x */
60, /* 1000 -> 6.0x */
70, /* 1001 -> 7.0x */
80, /* 1010 -> 8.0x */
50, /* 1011 -> 5.0x */
65, /* 1100 -> 6.5x */
75, /* 1101 -> 7.5x */
85, /* 1110 -> 8.5x */
120, /* 1111 -> 12.0x */
100, /* 0000 -> 10.0x */
-1, /* 0001 -> RESERVED */
120, /* 0010 -> 12.0x */
90, /* 0011 -> 9.0x */
105, /* 0100 -> 10.5x */
115, /* 0101 -> 11.5x */
125, /* 0110 -> 12.5x */
135, /* 0111 -> 13.5x */
140, /* 1000 -> 14.0x */
150, /* 1001 -> 15.0x */
160, /* 1010 -> 16.0x */
130, /* 1011 -> 13.0x */
145, /* 1100 -> 14.5x */
155, /* 1101 -> 15.5x */
-1, /* 1110 -> RESERVED (13.0x) */
120, /* 1111 -> 12.0x */
};
static int __initdata nehemiah_b_clock_ratio[32] = {
100, /* 0000 -> 10.0x */
160, /* 0001 -> 16.0x */
-1, /* 0010 -> RESERVED */
90, /* 0011 -> 9.0x */
95, /* 0100 -> 9.5x */
-1, /* 0101 -> RESERVED */
-1, /* 0110 -> RESERVED */
45, /* 0110 -> 4.5x */
55, /* 0111 -> 5.5x */
60, /* 1000 -> 6.0x */
70, /* 1001 -> 7.0x */
@ -305,84 +270,14 @@ static int __initdata nehemiah_b_clock_ratio[32] = {
120, /* 1111 -> 12.0x */
};
static int __initdata nehemiah_c_clock_ratio[32] = {
100, /* 0000 -> 10.0x */
160, /* 0001 -> 16.0x */
40, /* 0010 -> RESERVED */
90, /* 0011 -> 9.0x */
95, /* 0100 -> 9.5x */
-1, /* 0101 -> RESERVED */
45, /* 0110 -> RESERVED */
55, /* 0111 -> 5.5x */
60, /* 1000 -> 6.0x */
70, /* 1001 -> 7.0x */
80, /* 1010 -> 8.0x */
50, /* 1011 -> 5.0x */
65, /* 1100 -> 6.5x */
75, /* 1101 -> 7.5x */
85, /* 1110 -> 8.5x */
120, /* 1111 -> 12.0x */
100, /* 0000 -> 10.0x */
110, /* 0001 -> 11.0x */
120, /* 0010 -> 12.0x */
90, /* 0011 -> 9.0x */
105, /* 0100 -> 10.5x */
115, /* 0101 -> 11.5x */
125, /* 0110 -> 12.5x */
135, /* 0111 -> 13.5x */
140, /* 1000 -> 14.0x */
150, /* 1001 -> 15.0x */
160, /* 1010 -> 16.0x */
130, /* 1011 -> 13.0x */
145, /* 1100 -> 14.5x */
155, /* 1101 -> 15.5x */
-1, /* 1110 -> RESERVED (13.0x) */
120, /* 1111 -> 12.0x */
};
static int __initdata nehemiah_a_eblcr[32] = {
static int __initdata nehemiah_eblcr[32] = {
50, /* 0000 -> 5.0x */
160, /* 0001 -> 16.0x */
-1, /* 0010 -> RESERVED */
40, /* 0010 -> 4.0x */
100, /* 0011 -> 10.0x */
55, /* 0100 -> 5.5x */
-1, /* 0101 -> RESERVED */
-1, /* 0110 -> RESERVED */
95, /* 0111 -> 9.5x */
90, /* 1000 -> 9.0x */
70, /* 1001 -> 7.0x */
80, /* 1010 -> 8.0x */
60, /* 1011 -> 6.0x */
120, /* 1100 -> 12.0x */
75, /* 1101 -> 7.5x */
85, /* 1110 -> 8.5x */
65, /* 1111 -> 6.5x */
90, /* 0000 -> 9.0x */
-1, /* 0001 -> RESERVED */
120, /* 0010 -> 12.0x */
100, /* 0011 -> 10.0x */
135, /* 0100 -> 13.5x */
115, /* 0101 -> 11.5x */
125, /* 0110 -> 12.5x */
105, /* 0111 -> 10.5x */
130, /* 1000 -> 13.0x */
150, /* 1001 -> 15.0x */
160, /* 1010 -> 16.0x */
140, /* 1011 -> 14.0x */
120, /* 1100 -> 12.0x */
155, /* 1101 -> 15.5x */
-1, /* 1110 -> RESERVED (13.0x) */
145 /* 1111 -> 14.5x */
/* end of table */
};
static int __initdata nehemiah_b_eblcr[32] = {
50, /* 0000 -> 5.0x */
160, /* 0001 -> 16.0x */
-1, /* 0010 -> RESERVED */
100, /* 0011 -> 10.0x */
55, /* 0100 -> 5.5x */
-1, /* 0101 -> RESERVED */
-1, /* 0110 -> RESERVED */
45, /* 0110 -> 4.5x */
95, /* 0111 -> 9.5x */
90, /* 1000 -> 9.0x */
70, /* 1001 -> 7.0x */
@ -408,42 +303,6 @@ static int __initdata nehemiah_b_eblcr[32] = {
155, /* 1101 -> 15.5x */
-1, /* 1110 -> RESERVED (13.0x) */
145 /* 1111 -> 14.5x */
/* end of table */
};
static int __initdata nehemiah_c_eblcr[32] = {
50, /* 0000 -> 5.0x */
160, /* 0001 -> 16.0x */
40, /* 0010 -> RESERVED */
100, /* 0011 -> 10.0x */
55, /* 0100 -> 5.5x */
-1, /* 0101 -> RESERVED */
45, /* 0110 -> RESERVED */
95, /* 0111 -> 9.5x */
90, /* 1000 -> 9.0x */
70, /* 1001 -> 7.0x */
80, /* 1010 -> 8.0x */
60, /* 1011 -> 6.0x */
120, /* 1100 -> 12.0x */
75, /* 1101 -> 7.5x */
85, /* 1110 -> 8.5x */
65, /* 1111 -> 6.5x */
90, /* 0000 -> 9.0x */
110, /* 0001 -> 11.0x */
120, /* 0010 -> 12.0x */
100, /* 0011 -> 10.0x */
135, /* 0100 -> 13.5x */
115, /* 0101 -> 11.5x */
125, /* 0110 -> 12.5x */
105, /* 0111 -> 10.5x */
130, /* 1000 -> 13.0x */
150, /* 1001 -> 15.0x */
160, /* 1010 -> 16.0x */
140, /* 1011 -> 14.0x */
120, /* 1100 -> 12.0x */
155, /* 1101 -> 15.5x */
-1, /* 1110 -> RESERVED (13.0x) */
145 /* 1111 -> 14.5x */
/* end of table */
};
/*

View File

@ -1289,7 +1289,11 @@ static unsigned int powernowk8_get (unsigned int cpu)
if (query_current_values_with_pending_wait(data))
goto out;
khz = find_khz_freq_from_fid(data->currfid);
if (cpu_family == CPU_HW_PSTATE)
khz = find_khz_freq_from_fiddid(data->currfid, data->currdid);
else
khz = find_khz_freq_from_fid(data->currfid);
out:
set_cpus_allowed(current, oldmask);

View File

@ -6,6 +6,7 @@
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/timer.h>
#include <asm/pci-direct.h>
#include "cpu.h"
@ -161,19 +162,19 @@ static void __cpuinit set_cx86_inc(void)
static void __cpuinit geode_configure(void)
{
unsigned long flags;
u8 ccr3, ccr4;
u8 ccr3;
local_irq_save(flags);
/* Suspend on halt power saving and enable #SUSP pin */
setCx86(CX86_CCR2, getCx86(CX86_CCR2) | 0x88);
ccr3 = getCx86(CX86_CCR3);
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* Enable */
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
ccr4 = getCx86(CX86_CCR4);
ccr4 |= 0x38; /* FPU fast, DTE cache, Mem bypass */
setCx86(CX86_CCR3, ccr3);
/* FPU fast, DTE cache, Mem bypass */
setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x38);
setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
set_cx86_memwb();
set_cx86_reorder();
@ -183,14 +184,6 @@ static void __cpuinit geode_configure(void)
}
#ifdef CONFIG_PCI
static struct pci_device_id __cpuinitdata cyrix_55x0[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510) },
{ PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520) },
{ },
};
#endif
static void __cpuinit init_cyrix(struct cpuinfo_x86 *c)
{
unsigned char dir0, dir0_msn, dir0_lsn, dir1 = 0;
@ -258,6 +251,8 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c)
case 4: /* MediaGX/GXm or Geode GXM/GXLV/GX1 */
#ifdef CONFIG_PCI
{
u32 vendor, device;
/* It isn't really a PCI quirk directly, but the cure is the
same. The MediaGX has deep magic SMM stuff that handles the
SB emulation. It thows away the fifo on disable_dma() which
@ -273,22 +268,34 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c)
printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bugs.\n");
isa_dma_bridge_buggy = 2;
/* We do this before the PCI layer is running. However we
are safe here as we know the bridge must be a Cyrix
companion and must be present */
vendor = read_pci_config_16(0, 0, 0x12, PCI_VENDOR_ID);
device = read_pci_config_16(0, 0, 0x12, PCI_DEVICE_ID);
/*
* The 5510/5520 companion chips have a funky PIT.
*/
if (pci_dev_present(cyrix_55x0))
if (vendor == PCI_VENDOR_ID_CYRIX &&
(device == PCI_DEVICE_ID_CYRIX_5510 || device == PCI_DEVICE_ID_CYRIX_5520))
pit_latch_buggy = 1;
}
#endif
c->x86_cache_size=16; /* Yep 16K integrated cache thats it */
/* GXm supports extended cpuid levels 'ala' AMD */
if (c->cpuid_level == 2) {
/* Enable cxMMX extensions (GX1 Datasheet 54) */
setCx86(CX86_CCR7, getCx86(CX86_CCR7)|1);
setCx86(CX86_CCR7, getCx86(CX86_CCR7) | 1);
/* GXlv/GXm/GX1 */
if((dir1 >= 0x50 && dir1 <= 0x54) || dir1 >= 0x63)
/*
* GXm : 0x30 ... 0x5f GXm datasheet 51
* GXlv: 0x6x GXlv datasheet 54
* ? : 0x7x
* GX1 : 0x8x GX1 datasheet 56
*/
if((0x30 <= dir1 && dir1 <= 0x6f) || (0x80 <=dir1 && dir1 <= 0x8f))
geode_configure();
get_model_name(c); /* get CPU marketing name */
return;
@ -415,15 +422,14 @@ static void __cpuinit cyrix_identify(struct cpuinfo_x86 * c)
if (dir0 == 5 || dir0 == 3)
{
unsigned char ccr3, ccr4;
unsigned char ccr3;
unsigned long flags;
printk(KERN_INFO "Enabling CPUID on Cyrix processor.\n");
local_irq_save(flags);
ccr3 = getCx86(CX86_CCR3);
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
ccr4 = getCx86(CX86_CCR4);
setCx86(CX86_CCR4, ccr4 | 0x80); /* enable cpuid */
setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x80); /* enable cpuid */
setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
local_irq_restore(flags);
}
}

View File

@ -12,6 +12,7 @@
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/mce.h>
#include "mce.h"

View File

@ -1,4 +1,5 @@
#include <linux/init.h>
#include <asm/mce.h>
void amd_mcheck_init(struct cpuinfo_x86 *c);
void intel_p4_mcheck_init(struct cpuinfo_x86 *c);
@ -9,6 +10,5 @@ void winchip_mcheck_init(struct cpuinfo_x86 *c);
/* Call the installed machine check handler for this CPU setup. */
extern fastcall void (*machine_check_vector)(struct pt_regs *, long error_code);
extern int mce_disabled;
extern int nr_mce_banks;

View File

@ -12,6 +12,7 @@
#include <asm/system.h>
#include <asm/msr.h>
#include <asm/apic.h>
#include <asm/idle.h>
#include <asm/therm_throt.h>
@ -59,6 +60,7 @@ static void (*vendor_thermal_interrupt)(struct pt_regs *regs) = unexpected_therm
fastcall void smp_thermal_interrupt(struct pt_regs *regs)
{
exit_idle();
irq_enter();
vendor_thermal_interrupt(regs);
irq_exit();

View File

@ -211,6 +211,9 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
default:
return -ENOTTY;
case MTRRIOC_ADD_ENTRY:
#ifdef CONFIG_COMPAT
case MTRRIOC32_ADD_ENTRY:
#endif
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
err =
@ -218,21 +221,33 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
file, 0);
break;
case MTRRIOC_SET_ENTRY:
#ifdef CONFIG_COMPAT
case MTRRIOC32_SET_ENTRY:
#endif
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
err = mtrr_add(sentry.base, sentry.size, sentry.type, 0);
break;
case MTRRIOC_DEL_ENTRY:
#ifdef CONFIG_COMPAT
case MTRRIOC32_DEL_ENTRY:
#endif
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
err = mtrr_file_del(sentry.base, sentry.size, file, 0);
break;
case MTRRIOC_KILL_ENTRY:
#ifdef CONFIG_COMPAT
case MTRRIOC32_KILL_ENTRY:
#endif
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
err = mtrr_del(-1, sentry.base, sentry.size);
break;
case MTRRIOC_GET_ENTRY:
#ifdef CONFIG_COMPAT
case MTRRIOC32_GET_ENTRY:
#endif
if (gentry.regnum >= num_var_ranges)
return -EINVAL;
mtrr_if->get(gentry.regnum, &gentry.base, &size, &type);
@ -249,6 +264,9 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
break;
case MTRRIOC_ADD_PAGE_ENTRY:
#ifdef CONFIG_COMPAT
case MTRRIOC32_ADD_PAGE_ENTRY:
#endif
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
err =
@ -256,21 +274,33 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
file, 1);
break;
case MTRRIOC_SET_PAGE_ENTRY:
#ifdef CONFIG_COMPAT
case MTRRIOC32_SET_PAGE_ENTRY:
#endif
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
err = mtrr_add_page(sentry.base, sentry.size, sentry.type, 0);
break;
case MTRRIOC_DEL_PAGE_ENTRY:
#ifdef CONFIG_COMPAT
case MTRRIOC32_DEL_PAGE_ENTRY:
#endif
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
err = mtrr_file_del(sentry.base, sentry.size, file, 1);
break;
case MTRRIOC_KILL_PAGE_ENTRY:
#ifdef CONFIG_COMPAT
case MTRRIOC32_KILL_PAGE_ENTRY:
#endif
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
err = mtrr_del_page(-1, sentry.base, sentry.size);
break;
case MTRRIOC_GET_PAGE_ENTRY:
#ifdef CONFIG_COMPAT
case MTRRIOC32_GET_PAGE_ENTRY:
#endif
if (gentry.regnum >= num_var_ranges)
return -EINVAL;
mtrr_if->get(gentry.regnum, &gentry.base, &size, &type);

View File

@ -50,7 +50,7 @@ u32 num_var_ranges = 0;
unsigned int *usage_table;
static DEFINE_MUTEX(mtrr_mutex);
u32 size_or_mask, size_and_mask;
u64 size_or_mask, size_and_mask;
static struct mtrr_ops * mtrr_ops[X86_VENDOR_NUM] = {};
@ -662,8 +662,8 @@ void __init mtrr_bp_init(void)
boot_cpu_data.x86_mask == 0x4))
phys_addr = 36;
size_or_mask = ~((1 << (phys_addr - PAGE_SHIFT)) - 1);
size_and_mask = ~size_or_mask & 0xfff00000;
size_or_mask = ~((1ULL << (phys_addr - PAGE_SHIFT)) - 1);
size_and_mask = ~size_or_mask & 0xfffff00000ULL;
} else if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR &&
boot_cpu_data.x86 == 6) {
/* VIA C* family have Intel style MTRRs, but

View File

@ -84,7 +84,7 @@ void get_mtrr_state(void);
extern void set_mtrr_ops(struct mtrr_ops * ops);
extern u32 size_or_mask, size_and_mask;
extern u64 size_or_mask, size_and_mask;
extern struct mtrr_ops * mtrr_if;
#define is_cpu(vnd) (mtrr_if && mtrr_if->vendor == X86_VENDOR_##vnd)

View File

@ -29,7 +29,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL,
NULL, NULL, NULL, "mp", "nx", NULL, "mmxext", NULL,
NULL, "fxsr_opt", "rdtscp", NULL, NULL, "lm", "3dnowext", "3dnow",
NULL, "fxsr_opt", "pdpe1gb", "rdtscp", NULL, "lm", "3dnowext", "3dnow",
/* Transmeta-defined */
"recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL,
@ -47,7 +47,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
/* Intel-defined (#2) */
"pni", NULL, NULL, "monitor", "ds_cpl", "vmx", "smx", "est",
"tm2", "ssse3", "cid", NULL, NULL, "cx16", "xtpr", NULL,
NULL, NULL, "dca", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, "dca", NULL, NULL, NULL, NULL, "popcnt",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* VIA/Cyrix/Centaur-defined */
@ -57,8 +57,9 @@ static int show_cpuinfo(struct seq_file *m, void *v)
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* AMD-defined (#2) */
"lahf_lm", "cmp_legacy", "svm", NULL, "cr8legacy", NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
"lahf_lm", "cmp_legacy", "svm", "extapic", "cr8legacy", "abm",
"sse4a", "misalignsse",
"3dnowprefetch", "osvw", "ibs", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
@ -69,8 +70,11 @@ static int show_cpuinfo(struct seq_file *m, void *v)
"ttp", /* thermal trip */
"tm",
"stc",
"100mhzsteps",
"hwpstate",
NULL,
/* nothing */ /* constant_tsc - moved to flags */
NULL, /* constant_tsc - moved to flags */
/* nothing */
};
struct cpuinfo_x86 *c = v;
int i, n = c - cpu_data;

View File

@ -9,7 +9,7 @@ static void __cpuinit init_transmeta(struct cpuinfo_x86 *c)
{
unsigned int cap_mask, uk, max, dummy;
unsigned int cms_rev1, cms_rev2;
unsigned int cpu_rev, cpu_freq, cpu_flags, new_cpu_rev;
unsigned int cpu_rev, cpu_freq = 0, cpu_flags, new_cpu_rev;
char cpu_info[65];
get_model_name(c); /* Same as AMD/Cyrix */
@ -72,6 +72,9 @@ static void __cpuinit init_transmeta(struct cpuinfo_x86 *c)
wrmsr(0x80860004, ~0, uk);
c->x86_capability[0] = cpuid_edx(0x00000001);
wrmsr(0x80860004, cap_mask, uk);
/* All Transmeta CPUs have a constant TSC */
set_bit(X86_FEATURE_CONSTANT_TSC, c->x86_capability);
/* If we can run i686 user-space code, call us an i686 */
#define USER686 (X86_FEATURE_TSC|X86_FEATURE_CX8|X86_FEATURE_CMOV)

View File

@ -48,7 +48,6 @@ static struct class *cpuid_class;
#ifdef CONFIG_SMP
struct cpuid_command {
int cpu;
u32 reg;
u32 *data;
};
@ -57,8 +56,7 @@ static void cpuid_smp_cpuid(void *cmd_block)
{
struct cpuid_command *cmd = (struct cpuid_command *)cmd_block;
if (cmd->cpu == smp_processor_id())
cpuid(cmd->reg, &cmd->data[0], &cmd->data[1], &cmd->data[2],
cpuid(cmd->reg, &cmd->data[0], &cmd->data[1], &cmd->data[2],
&cmd->data[3]);
}
@ -70,11 +68,10 @@ static inline void do_cpuid(int cpu, u32 reg, u32 * data)
if (cpu == smp_processor_id()) {
cpuid(reg, &data[0], &data[1], &data[2], &data[3]);
} else {
cmd.cpu = cpu;
cmd.reg = reg;
cmd.data = data;
smp_call_function(cpuid_smp_cpuid, &cmd, 1, 1);
smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1, 1);
}
preempt_enable();
}

View File

@ -14,6 +14,7 @@
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/e820.h>
#include <asm/setup.h>
#ifdef CONFIG_EFI
int efi_enabled = 0;
@ -156,21 +157,22 @@ static struct resource standard_io_resources[] = { {
.flags = IORESOURCE_BUSY | IORESOURCE_IO
} };
static int romsignature(const unsigned char *x)
#define ROMSIGNATURE 0xaa55
static int __init romsignature(const unsigned char *rom)
{
unsigned short sig;
int ret = 0;
if (probe_kernel_address((const unsigned short *)x, sig) == 0)
ret = (sig == 0xaa55);
return ret;
return probe_kernel_address((const unsigned short *)rom, sig) == 0 &&
sig == ROMSIGNATURE;
}
static int __init romchecksum(unsigned char *rom, unsigned long length)
{
unsigned char *p, sum = 0;
unsigned char sum;
for (p = rom; p < rom + length; p++)
sum += *p;
for (sum = 0; length; length--)
sum += *rom++;
return sum == 0;
}

View File

@ -30,7 +30,7 @@
* 18(%esp) - %eax
* 1C(%esp) - %ds
* 20(%esp) - %es
* 24(%esp) - %gs
* 24(%esp) - %fs
* 28(%esp) - orig_eax
* 2C(%esp) - %eip
* 30(%esp) - %cs
@ -99,9 +99,9 @@ VM_MASK = 0x00020000
#define SAVE_ALL \
cld; \
pushl %gs; \
pushl %fs; \
CFI_ADJUST_CFA_OFFSET 4;\
/*CFI_REL_OFFSET gs, 0;*/\
/*CFI_REL_OFFSET fs, 0;*/\
pushl %es; \
CFI_ADJUST_CFA_OFFSET 4;\
/*CFI_REL_OFFSET es, 0;*/\
@ -133,7 +133,7 @@ VM_MASK = 0x00020000
movl %edx, %ds; \
movl %edx, %es; \
movl $(__KERNEL_PDA), %edx; \
movl %edx, %gs
movl %edx, %fs
#define RESTORE_INT_REGS \
popl %ebx; \
@ -166,9 +166,9 @@ VM_MASK = 0x00020000
2: popl %es; \
CFI_ADJUST_CFA_OFFSET -4;\
/*CFI_RESTORE es;*/\
3: popl %gs; \
3: popl %fs; \
CFI_ADJUST_CFA_OFFSET -4;\
/*CFI_RESTORE gs;*/\
/*CFI_RESTORE fs;*/\
.pushsection .fixup,"ax"; \
4: movl $0,(%esp); \
jmp 1b; \
@ -227,6 +227,7 @@ ENTRY(ret_from_fork)
CFI_ADJUST_CFA_OFFSET -4
jmp syscall_exit
CFI_ENDPROC
END(ret_from_fork)
/*
* Return to user mode is not as complex as all this looks,
@ -258,6 +259,7 @@ ENTRY(resume_userspace)
# int/exception return?
jne work_pending
jmp restore_all
END(ret_from_exception)
#ifdef CONFIG_PREEMPT
ENTRY(resume_kernel)
@ -272,6 +274,7 @@ need_resched:
jz restore_all
call preempt_schedule_irq
jmp need_resched
END(resume_kernel)
#endif
CFI_ENDPROC
@ -349,16 +352,17 @@ sysenter_past_esp:
movl PT_OLDESP(%esp), %ecx
xorl %ebp,%ebp
TRACE_IRQS_ON
1: mov PT_GS(%esp), %gs
1: mov PT_FS(%esp), %fs
ENABLE_INTERRUPTS_SYSEXIT
CFI_ENDPROC
.pushsection .fixup,"ax"
2: movl $0,PT_GS(%esp)
2: movl $0,PT_FS(%esp)
jmp 1b
.section __ex_table,"a"
.align 4
.long 1b,2b
.popsection
ENDPROC(sysenter_entry)
# system call handler stub
ENTRY(system_call)
@ -459,6 +463,7 @@ ldt_ss:
CFI_ADJUST_CFA_OFFSET -8
jmp restore_nocheck
CFI_ENDPROC
ENDPROC(system_call)
# perform work that needs to be done immediately before resumption
ALIGN
@ -504,6 +509,7 @@ work_notifysig_v86:
xorl %edx, %edx
call do_notify_resume
jmp resume_userspace_sig
END(work_pending)
# perform syscall exit tracing
ALIGN
@ -519,6 +525,7 @@ syscall_trace_entry:
cmpl $(nr_syscalls), %eax
jnae syscall_call
jmp syscall_exit
END(syscall_trace_entry)
# perform syscall exit tracing
ALIGN
@ -532,6 +539,7 @@ syscall_exit_work:
movl $1, %edx
call do_syscall_trace
jmp resume_userspace
END(syscall_exit_work)
CFI_ENDPROC
RING0_INT_FRAME # can't unwind into user space anyway
@ -542,15 +550,17 @@ syscall_fault:
GET_THREAD_INFO(%ebp)
movl $-EFAULT,PT_EAX(%esp)
jmp resume_userspace
END(syscall_fault)
syscall_badsys:
movl $-ENOSYS,PT_EAX(%esp)
jmp resume_userspace
END(syscall_badsys)
CFI_ENDPROC
#define FIXUP_ESPFIX_STACK \
/* since we are on a wrong stack, we cant make it a C code :( */ \
movl %gs:PDA_cpu, %ebx; \
movl %fs:PDA_cpu, %ebx; \
PER_CPU(cpu_gdt_descr, %ebx); \
movl GDS_address(%ebx), %ebx; \
GET_DESC_BASE(GDT_ENTRY_ESPFIX_SS, %ebx, %eax, %ax, %al, %ah); \
@ -581,9 +591,9 @@ syscall_badsys:
ENTRY(interrupt)
.text
vector=0
ENTRY(irq_entries_start)
RING0_INT_FRAME
vector=0
.rept NR_IRQS
ALIGN
.if vector
@ -592,11 +602,16 @@ ENTRY(irq_entries_start)
1: pushl $~(vector)
CFI_ADJUST_CFA_OFFSET 4
jmp common_interrupt
.data
.previous
.long 1b
.text
.text
vector=vector+1
.endr
END(irq_entries_start)
.previous
END(interrupt)
.previous
/*
* the CPU automatically disables interrupts when executing an IRQ vector,
@ -609,6 +624,7 @@ common_interrupt:
movl %esp,%eax
call do_IRQ
jmp ret_from_intr
ENDPROC(common_interrupt)
CFI_ENDPROC
#define BUILD_INTERRUPT(name, nr) \
@ -621,18 +637,24 @@ ENTRY(name) \
movl %esp,%eax; \
call smp_/**/name; \
jmp ret_from_intr; \
CFI_ENDPROC
CFI_ENDPROC; \
ENDPROC(name)
/* The include is where all of the SMP etc. interrupts come from */
#include "entry_arch.h"
/* This alternate entry is needed because we hijack the apic LVTT */
#if defined(CONFIG_VMI) && defined(CONFIG_X86_LOCAL_APIC)
BUILD_INTERRUPT(apic_vmi_timer_interrupt,LOCAL_TIMER_VECTOR)
#endif
KPROBE_ENTRY(page_fault)
RING0_EC_FRAME
pushl $do_page_fault
CFI_ADJUST_CFA_OFFSET 4
ALIGN
error_code:
/* the function address is in %gs's slot on the stack */
/* the function address is in %fs's slot on the stack */
pushl %es
CFI_ADJUST_CFA_OFFSET 4
/*CFI_REL_OFFSET es, 0*/
@ -661,20 +683,20 @@ error_code:
CFI_ADJUST_CFA_OFFSET 4
CFI_REL_OFFSET ebx, 0
cld
pushl %gs
pushl %fs
CFI_ADJUST_CFA_OFFSET 4
/*CFI_REL_OFFSET gs, 0*/
/*CFI_REL_OFFSET fs, 0*/
movl $(__KERNEL_PDA), %ecx
movl %ecx, %gs
movl %ecx, %fs
UNWIND_ESPFIX_STACK
popl %ecx
CFI_ADJUST_CFA_OFFSET -4
/*CFI_REGISTER es, ecx*/
movl PT_GS(%esp), %edi # get the function address
movl PT_FS(%esp), %edi # get the function address
movl PT_ORIG_EAX(%esp), %edx # get the error code
movl $-1, PT_ORIG_EAX(%esp) # no syscall to restart
mov %ecx, PT_GS(%esp)
/*CFI_REL_OFFSET gs, ES*/
mov %ecx, PT_FS(%esp)
/*CFI_REL_OFFSET fs, ES*/
movl $(__USER_DS), %ecx
movl %ecx, %ds
movl %ecx, %es
@ -692,6 +714,7 @@ ENTRY(coprocessor_error)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(coprocessor_error)
ENTRY(simd_coprocessor_error)
RING0_INT_FRAME
@ -701,6 +724,7 @@ ENTRY(simd_coprocessor_error)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(simd_coprocessor_error)
ENTRY(device_not_available)
RING0_INT_FRAME
@ -721,6 +745,7 @@ device_not_available_emulate:
CFI_ADJUST_CFA_OFFSET -4
jmp ret_from_exception
CFI_ENDPROC
END(device_not_available)
/*
* Debug traps and NMI can happen at the one SYSENTER instruction
@ -864,10 +889,12 @@ ENTRY(native_iret)
.align 4
.long 1b,iret_exc
.previous
END(native_iret)
ENTRY(native_irq_enable_sysexit)
sti
sysexit
END(native_irq_enable_sysexit)
#endif
KPROBE_ENTRY(int3)
@ -890,6 +917,7 @@ ENTRY(overflow)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(overflow)
ENTRY(bounds)
RING0_INT_FRAME
@ -899,6 +927,7 @@ ENTRY(bounds)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(bounds)
ENTRY(invalid_op)
RING0_INT_FRAME
@ -908,6 +937,7 @@ ENTRY(invalid_op)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(invalid_op)
ENTRY(coprocessor_segment_overrun)
RING0_INT_FRAME
@ -917,6 +947,7 @@ ENTRY(coprocessor_segment_overrun)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(coprocessor_segment_overrun)
ENTRY(invalid_TSS)
RING0_EC_FRAME
@ -924,6 +955,7 @@ ENTRY(invalid_TSS)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(invalid_TSS)
ENTRY(segment_not_present)
RING0_EC_FRAME
@ -931,6 +963,7 @@ ENTRY(segment_not_present)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(segment_not_present)
ENTRY(stack_segment)
RING0_EC_FRAME
@ -938,6 +971,7 @@ ENTRY(stack_segment)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(stack_segment)
KPROBE_ENTRY(general_protection)
RING0_EC_FRAME
@ -953,6 +987,7 @@ ENTRY(alignment_check)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(alignment_check)
ENTRY(divide_error)
RING0_INT_FRAME
@ -962,6 +997,7 @@ ENTRY(divide_error)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(divide_error)
#ifdef CONFIG_X86_MCE
ENTRY(machine_check)
@ -972,6 +1008,7 @@ ENTRY(machine_check)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(machine_check)
#endif
ENTRY(spurious_interrupt_bug)
@ -982,6 +1019,7 @@ ENTRY(spurious_interrupt_bug)
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(spurious_interrupt_bug)
ENTRY(kernel_thread_helper)
pushl $0 # fake return address for unwinder

View File

@ -53,6 +53,7 @@
* any particular GDT layout, because we load our own as soon as we
* can.
*/
.section .text.head,"ax",@progbits
ENTRY(startup_32)
#ifdef CONFIG_PARAVIRT
@ -141,16 +142,25 @@ page_pde_offset = (__PAGE_OFFSET >> 20);
jb 10b
movl %edi,(init_pg_tables_end - __PAGE_OFFSET)
#ifdef CONFIG_SMP
xorl %ebx,%ebx /* This is the boot CPU (BSP) */
jmp 3f
/*
* Non-boot CPU entry point; entered from trampoline.S
* We can't lgdt here, because lgdt itself uses a data segment, but
* we know the trampoline has already loaded the boot_gdt_table GDT
* for us.
*
* If cpu hotplug is not supported then this code can go in init section
* which will be freed later
*/
#ifdef CONFIG_HOTPLUG_CPU
.section .text,"ax",@progbits
#else
.section .init.text,"ax",@progbits
#endif
#ifdef CONFIG_SMP
ENTRY(startup_32_smp)
cld
movl $(__BOOT_DS),%eax
@ -208,8 +218,8 @@ ENTRY(startup_32_smp)
xorl %ebx,%ebx
incl %ebx
3:
#endif /* CONFIG_SMP */
3:
/*
* Enable paging
@ -309,7 +319,7 @@ is386: movl $2,%ecx # set MP
call check_x87
call setup_pda
lgdt cpu_gdt_descr
lgdt early_gdt_descr
lidt idt_descr
ljmp $(__KERNEL_CS),$1f
1: movl $(__KERNEL_DS),%eax # reload all the segment registers
@ -319,12 +329,12 @@ is386: movl $2,%ecx # set MP
movl %eax,%ds
movl %eax,%es
xorl %eax,%eax # Clear FS and LDT
movl %eax,%fs
xorl %eax,%eax # Clear GS and LDT
movl %eax,%gs
lldt %ax
movl $(__KERNEL_PDA),%eax
mov %eax,%gs
mov %eax,%fs
cld # gcc2 wants the direction flag cleared at all times
pushl $0 # fake return address for unwinder
@ -360,12 +370,12 @@ check_x87:
* cpu_gdt_table and boot_pda; for secondary CPUs, these will be
* that CPU's GDT and PDA.
*/
setup_pda:
ENTRY(setup_pda)
/* get the PDA pointer */
movl start_pda, %eax
/* slot the PDA address into the GDT */
mov cpu_gdt_descr+2, %ecx
mov early_gdt_descr+2, %ecx
mov %ax, (__KERNEL_PDA+0+2)(%ecx) /* base & 0x0000ffff */
shr $16, %eax
mov %al, (__KERNEL_PDA+4+0)(%ecx) /* base & 0x00ff0000 */
@ -492,6 +502,7 @@ ignore_int:
#endif
iret
.section .text
#ifdef CONFIG_PARAVIRT
startup_paravirt:
cld
@ -502,10 +513,11 @@ startup_paravirt:
pushl %ecx
pushl %eax
/* paravirt.o is last in link, and that probe fn never returns */
pushl $__start_paravirtprobe
1:
movl 0(%esp), %eax
cmpl $__stop_paravirtprobe, %eax
je unhandled_paravirt
pushl (%eax)
movl 8(%esp), %eax
call *(%esp)
@ -517,6 +529,10 @@ startup_paravirt:
addl $4, (%esp)
jmp 1b
unhandled_paravirt:
/* Nothing wanted us: we're screwed. */
ud2
#endif
/*
@ -581,7 +597,7 @@ idt_descr:
# boot GDT descriptor (later on used by CPU#0):
.word 0 # 32 bit align gdt_desc.address
ENTRY(cpu_gdt_descr)
ENTRY(early_gdt_descr)
.word GDT_ENTRIES*8-1
.long cpu_gdt_table

View File

@ -1,4 +1,5 @@
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/errno.h>
#include <linux/hpet.h>
#include <linux/init.h>
@ -6,17 +7,278 @@
#include <asm/hpet.h>
#include <asm/io.h>
extern struct clock_event_device *global_clock_event;
#define HPET_MASK CLOCKSOURCE_MASK(32)
#define HPET_SHIFT 22
/* FSEC = 10^-15 NSEC = 10^-9 */
#define FSEC_PER_NSEC 1000000
static void __iomem *hpet_ptr;
/*
* HPET address is set in acpi/boot.c, when an ACPI entry exists
*/
unsigned long hpet_address;
static void __iomem * hpet_virt_address;
static inline unsigned long hpet_readl(unsigned long a)
{
return readl(hpet_virt_address + a);
}
static inline void hpet_writel(unsigned long d, unsigned long a)
{
writel(d, hpet_virt_address + a);
}
/*
* HPET command line enable / disable
*/
static int boot_hpet_disable;
static int __init hpet_setup(char* str)
{
if (str) {
if (!strncmp("disable", str, 7))
boot_hpet_disable = 1;
}
return 1;
}
__setup("hpet=", hpet_setup);
static inline int is_hpet_capable(void)
{
return (!boot_hpet_disable && hpet_address);
}
/*
* HPET timer interrupt enable / disable
*/
static int hpet_legacy_int_enabled;
/**
* is_hpet_enabled - check whether the hpet timer interrupt is enabled
*/
int is_hpet_enabled(void)
{
return is_hpet_capable() && hpet_legacy_int_enabled;
}
/*
* When the hpet driver (/dev/hpet) is enabled, we need to reserve
* timer 0 and timer 1 in case of RTC emulation.
*/
#ifdef CONFIG_HPET
static void hpet_reserve_platform_timers(unsigned long id)
{
struct hpet __iomem *hpet = hpet_virt_address;
struct hpet_timer __iomem *timer = &hpet->hpet_timers[2];
unsigned int nrtimers, i;
struct hpet_data hd;
nrtimers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1;
memset(&hd, 0, sizeof (hd));
hd.hd_phys_address = hpet_address;
hd.hd_address = hpet_virt_address;
hd.hd_nirqs = nrtimers;
hd.hd_flags = HPET_DATA_PLATFORM;
hpet_reserve_timer(&hd, 0);
#ifdef CONFIG_HPET_EMULATE_RTC
hpet_reserve_timer(&hd, 1);
#endif
hd.hd_irq[0] = HPET_LEGACY_8254;
hd.hd_irq[1] = HPET_LEGACY_RTC;
for (i = 2; i < nrtimers; timer++, i++)
hd.hd_irq[i] = (timer->hpet_config & Tn_INT_ROUTE_CNF_MASK) >>
Tn_INT_ROUTE_CNF_SHIFT;
hpet_alloc(&hd);
}
#else
static void hpet_reserve_platform_timers(unsigned long id) { }
#endif
/*
* Common hpet info
*/
static unsigned long hpet_period;
static void hpet_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt);
static int hpet_next_event(unsigned long delta,
struct clock_event_device *evt);
/*
* The hpet clock event device
*/
static struct clock_event_device hpet_clockevent = {
.name = "hpet",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_mode = hpet_set_mode,
.set_next_event = hpet_next_event,
.shift = 32,
.irq = 0,
};
static void hpet_start_counter(void)
{
unsigned long cfg = hpet_readl(HPET_CFG);
cfg &= ~HPET_CFG_ENABLE;
hpet_writel(cfg, HPET_CFG);
hpet_writel(0, HPET_COUNTER);
hpet_writel(0, HPET_COUNTER + 4);
cfg |= HPET_CFG_ENABLE;
hpet_writel(cfg, HPET_CFG);
}
static void hpet_enable_int(void)
{
unsigned long cfg = hpet_readl(HPET_CFG);
cfg |= HPET_CFG_LEGACY;
hpet_writel(cfg, HPET_CFG);
hpet_legacy_int_enabled = 1;
}
static void hpet_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
unsigned long cfg, cmp, now;
uint64_t delta;
switch(mode) {
case CLOCK_EVT_MODE_PERIODIC:
delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * hpet_clockevent.mult;
delta >>= hpet_clockevent.shift;
now = hpet_readl(HPET_COUNTER);
cmp = now + (unsigned long) delta;
cfg = hpet_readl(HPET_T0_CFG);
cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
HPET_TN_SETVAL | HPET_TN_32BIT;
hpet_writel(cfg, HPET_T0_CFG);
/*
* The first write after writing TN_SETVAL to the
* config register sets the counter value, the second
* write sets the period.
*/
hpet_writel(cmp, HPET_T0_CMP);
udelay(1);
hpet_writel((unsigned long) delta, HPET_T0_CMP);
break;
case CLOCK_EVT_MODE_ONESHOT:
cfg = hpet_readl(HPET_T0_CFG);
cfg &= ~HPET_TN_PERIODIC;
cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
hpet_writel(cfg, HPET_T0_CFG);
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
cfg = hpet_readl(HPET_T0_CFG);
cfg &= ~HPET_TN_ENABLE;
hpet_writel(cfg, HPET_T0_CFG);
break;
}
}
static int hpet_next_event(unsigned long delta,
struct clock_event_device *evt)
{
unsigned long cnt;
cnt = hpet_readl(HPET_COUNTER);
cnt += delta;
hpet_writel(cnt, HPET_T0_CMP);
return ((long)(hpet_readl(HPET_COUNTER) - cnt ) > 0);
}
/*
* Try to setup the HPET timer
*/
int __init hpet_enable(void)
{
unsigned long id;
uint64_t hpet_freq;
if (!is_hpet_capable())
return 0;
hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE);
/*
* Read the period and check for a sane value:
*/
hpet_period = hpet_readl(HPET_PERIOD);
if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD)
goto out_nohpet;
/*
* The period is a femto seconds value. We need to calculate the
* scaled math multiplication factor for nanosecond to hpet tick
* conversion.
*/
hpet_freq = 1000000000000000ULL;
do_div(hpet_freq, hpet_period);
hpet_clockevent.mult = div_sc((unsigned long) hpet_freq,
NSEC_PER_SEC, 32);
/* Calculate the min / max delta */
hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF,
&hpet_clockevent);
hpet_clockevent.min_delta_ns = clockevent_delta2ns(0x30,
&hpet_clockevent);
/*
* Read the HPET ID register to retrieve the IRQ routing
* information and the number of channels
*/
id = hpet_readl(HPET_ID);
#ifdef CONFIG_HPET_EMULATE_RTC
/*
* The legacy routing mode needs at least two channels, tick timer
* and the rtc emulation channel.
*/
if (!(id & HPET_ID_NUMBER))
goto out_nohpet;
#endif
/* Start the counter */
hpet_start_counter();
if (id & HPET_ID_LEGSUP) {
hpet_enable_int();
hpet_reserve_platform_timers(id);
/*
* Start hpet with the boot cpu mask and make it
* global after the IO_APIC has been initialized.
*/
hpet_clockevent.cpumask =cpumask_of_cpu(0);
clockevents_register_device(&hpet_clockevent);
global_clock_event = &hpet_clockevent;
return 1;
}
return 0;
out_nohpet:
iounmap(hpet_virt_address);
hpet_virt_address = NULL;
return 0;
}
/*
* Clock source related code
*/
static cycle_t read_hpet(void)
{
return (cycle_t)readl(hpet_ptr);
return (cycle_t)hpet_readl(HPET_COUNTER);
}
static struct clocksource clocksource_hpet = {
@ -24,28 +286,17 @@ static struct clocksource clocksource_hpet = {
.rating = 250,
.read = read_hpet,
.mask = HPET_MASK,
.mult = 0, /* set below */
.shift = HPET_SHIFT,
.is_continuous = 1,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int __init init_hpet_clocksource(void)
{
unsigned long hpet_period;
void __iomem* hpet_base;
u64 tmp;
int err;
if (!is_hpet_enabled())
if (!hpet_virt_address)
return -ENODEV;
/* calculate the hpet address: */
hpet_base = ioremap_nocache(hpet_address, HPET_MMAP_SIZE);
hpet_ptr = hpet_base + HPET_COUNTER;
/* calculate the frequency: */
hpet_period = readl(hpet_base + HPET_PERIOD);
/*
* hpet period is in femto seconds per cycle
* so we need to convert this to ns/cyc units
@ -61,11 +312,218 @@ static int __init init_hpet_clocksource(void)
do_div(tmp, FSEC_PER_NSEC);
clocksource_hpet.mult = (u32)tmp;
err = clocksource_register(&clocksource_hpet);
if (err)
iounmap(hpet_base);
return err;
return clocksource_register(&clocksource_hpet);
}
module_init(init_hpet_clocksource);
#ifdef CONFIG_HPET_EMULATE_RTC
/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET
* is enabled, we support RTC interrupt functionality in software.
* RTC has 3 kinds of interrupts:
* 1) Update Interrupt - generate an interrupt, every sec, when RTC clock
* is updated
* 2) Alarm Interrupt - generate an interrupt at a specific time of day
* 3) Periodic Interrupt - generate periodic interrupt, with frequencies
* 2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2)
* (1) and (2) above are implemented using polling at a frequency of
* 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt
* overhead. (DEFAULT_RTC_INT_FREQ)
* For (3), we use interrupts at 64Hz or user specified periodic
* frequency, whichever is higher.
*/
#include <linux/mc146818rtc.h>
#include <linux/rtc.h>
#define DEFAULT_RTC_INT_FREQ 64
#define DEFAULT_RTC_SHIFT 6
#define RTC_NUM_INTS 1
static unsigned long hpet_rtc_flags;
static unsigned long hpet_prev_update_sec;
static struct rtc_time hpet_alarm_time;
static unsigned long hpet_pie_count;
static unsigned long hpet_t1_cmp;
static unsigned long hpet_default_delta;
static unsigned long hpet_pie_delta;
static unsigned long hpet_pie_limit;
/*
* Timer 1 for RTC emulation. We use one shot mode, as periodic mode
* is not supported by all HPET implementations for timer 1.
*
* hpet_rtc_timer_init() is called when the rtc is initialized.
*/
int hpet_rtc_timer_init(void)
{
unsigned long cfg, cnt, delta, flags;
if (!is_hpet_enabled())
return 0;
if (!hpet_default_delta) {
uint64_t clc;
clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC;
clc >>= hpet_clockevent.shift + DEFAULT_RTC_SHIFT;
hpet_default_delta = (unsigned long) clc;
}
if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit)
delta = hpet_default_delta;
else
delta = hpet_pie_delta;
local_irq_save(flags);
cnt = delta + hpet_readl(HPET_COUNTER);
hpet_writel(cnt, HPET_T1_CMP);
hpet_t1_cmp = cnt;
cfg = hpet_readl(HPET_T1_CFG);
cfg &= ~HPET_TN_PERIODIC;
cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
hpet_writel(cfg, HPET_T1_CFG);
local_irq_restore(flags);
return 1;
}
/*
* The functions below are called from rtc driver.
* Return 0 if HPET is not being used.
* Otherwise do the necessary changes and return 1.
*/
int hpet_mask_rtc_irq_bit(unsigned long bit_mask)
{
if (!is_hpet_enabled())
return 0;
hpet_rtc_flags &= ~bit_mask;
return 1;
}
int hpet_set_rtc_irq_bit(unsigned long bit_mask)
{
unsigned long oldbits = hpet_rtc_flags;
if (!is_hpet_enabled())
return 0;
hpet_rtc_flags |= bit_mask;
if (!oldbits)
hpet_rtc_timer_init();
return 1;
}
int hpet_set_alarm_time(unsigned char hrs, unsigned char min,
unsigned char sec)
{
if (!is_hpet_enabled())
return 0;
hpet_alarm_time.tm_hour = hrs;
hpet_alarm_time.tm_min = min;
hpet_alarm_time.tm_sec = sec;
return 1;
}
int hpet_set_periodic_freq(unsigned long freq)
{
uint64_t clc;
if (!is_hpet_enabled())
return 0;
if (freq <= DEFAULT_RTC_INT_FREQ)
hpet_pie_limit = DEFAULT_RTC_INT_FREQ / freq;
else {
clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC;
do_div(clc, freq);
clc >>= hpet_clockevent.shift;
hpet_pie_delta = (unsigned long) clc;
}
return 1;
}
int hpet_rtc_dropped_irq(void)
{
return is_hpet_enabled();
}
static void hpet_rtc_timer_reinit(void)
{
unsigned long cfg, delta;
int lost_ints = -1;
if (unlikely(!hpet_rtc_flags)) {
cfg = hpet_readl(HPET_T1_CFG);
cfg &= ~HPET_TN_ENABLE;
hpet_writel(cfg, HPET_T1_CFG);
return;
}
if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit)
delta = hpet_default_delta;
else
delta = hpet_pie_delta;
/*
* Increment the comparator value until we are ahead of the
* current count.
*/
do {
hpet_t1_cmp += delta;
hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
lost_ints++;
} while ((long)(hpet_readl(HPET_COUNTER) - hpet_t1_cmp) > 0);
if (lost_ints) {
if (hpet_rtc_flags & RTC_PIE)
hpet_pie_count += lost_ints;
if (printk_ratelimit())
printk(KERN_WARNING "rtc: lost %d interrupts\n",
lost_ints);
}
}
irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id)
{
struct rtc_time curr_time;
unsigned long rtc_int_flag = 0;
hpet_rtc_timer_reinit();
if (hpet_rtc_flags & (RTC_UIE | RTC_AIE))
rtc_get_rtc_time(&curr_time);
if (hpet_rtc_flags & RTC_UIE &&
curr_time.tm_sec != hpet_prev_update_sec) {
rtc_int_flag = RTC_UF;
hpet_prev_update_sec = curr_time.tm_sec;
}
if (hpet_rtc_flags & RTC_PIE &&
++hpet_pie_count >= hpet_pie_limit) {
rtc_int_flag |= RTC_PF;
hpet_pie_count = 0;
}
if (hpet_rtc_flags & RTC_PIE &&
(curr_time.tm_sec == hpet_alarm_time.tm_sec) &&
(curr_time.tm_min == hpet_alarm_time.tm_min) &&
(curr_time.tm_hour == hpet_alarm_time.tm_hour))
rtc_int_flag |= RTC_AF;
if (rtc_int_flag) {
rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8));
rtc_interrupt(rtc_int_flag, dev_id);
}
return IRQ_HANDLED;
}
#endif

View File

@ -2,7 +2,7 @@
* i8253.c 8253/PIT functions
*
*/
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
#include <linux/sysdev.h>
@ -19,19 +19,99 @@
DEFINE_SPINLOCK(i8253_lock);
EXPORT_SYMBOL(i8253_lock);
void setup_pit_timer(void)
/*
* HPET replaces the PIT, when enabled. So we need to know, which of
* the two timers is used
*/
struct clock_event_device *global_clock_event;
/*
* Initialize the PIT timer.
*
* This is also called after resume to bring the PIT into operation again.
*/
static void init_pit_timer(enum clock_event_mode mode,
struct clock_event_device *evt)
{
unsigned long flags;
spin_lock_irqsave(&i8253_lock, flags);
outb_p(0x34,PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */
udelay(10);
outb_p(LATCH & 0xff , PIT_CH0); /* LSB */
udelay(10);
outb(LATCH >> 8 , PIT_CH0); /* MSB */
switch(mode) {
case CLOCK_EVT_MODE_PERIODIC:
/* binary, mode 2, LSB/MSB, ch 0 */
outb_p(0x34, PIT_MODE);
udelay(10);
outb_p(LATCH & 0xff , PIT_CH0); /* LSB */
udelay(10);
outb(LATCH >> 8 , PIT_CH0); /* MSB */
break;
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
/* One shot setup */
outb_p(0x38, PIT_MODE);
udelay(10);
break;
}
spin_unlock_irqrestore(&i8253_lock, flags);
}
/*
* Program the next event in oneshot mode
*
* Delta is given in PIT ticks
*/
static int pit_next_event(unsigned long delta, struct clock_event_device *evt)
{
unsigned long flags;
spin_lock_irqsave(&i8253_lock, flags);
outb_p(delta & 0xff , PIT_CH0); /* LSB */
outb(delta >> 8 , PIT_CH0); /* MSB */
spin_unlock_irqrestore(&i8253_lock, flags);
return 0;
}
/*
* On UP the PIT can serve all of the possible timer functions. On SMP systems
* it can be solely used for the global tick.
*
* The profiling and update capabilites are switched off once the local apic is
* registered. This mechanism replaces the previous #ifdef LOCAL_APIC -
* !using_apic_timer decisions in do_timer_interrupt_hook()
*/
struct clock_event_device pit_clockevent = {
.name = "pit",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_mode = init_pit_timer,
.set_next_event = pit_next_event,
.shift = 32,
.irq = 0,
};
/*
* Initialize the conversion factor and the min/max deltas of the clock event
* structure and register the clock event source with the framework.
*/
void __init setup_pit_timer(void)
{
/*
* Start pit with the boot cpu mask and make it global after the
* IO_APIC has been initialized.
*/
pit_clockevent.cpumask = cpumask_of_cpu(0);
pit_clockevent.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, 32);
pit_clockevent.max_delta_ns =
clockevent_delta2ns(0x7FFF, &pit_clockevent);
pit_clockevent.min_delta_ns =
clockevent_delta2ns(0xF, &pit_clockevent);
clockevents_register_device(&pit_clockevent);
global_clock_event = &pit_clockevent;
}
/*
* Since the PIT overflows every tick, its not very useful
* to just read by itself. So use jiffies to emulate a free
@ -46,7 +126,7 @@ static cycle_t pit_read(void)
static u32 old_jifs;
spin_lock_irqsave(&i8253_lock, flags);
/*
/*
* Although our caller may have the read side of xtime_lock,
* this is now a seqlock, and we are cheating in this routine
* by having side effects on state that we cannot undo if

View File

@ -41,6 +41,7 @@ static void mask_and_ack_8259A(unsigned int);
static struct irq_chip i8259A_chip = {
.name = "XT-PIC",
.mask = disable_8259A_irq,
.disable = disable_8259A_irq,
.unmask = enable_8259A_irq,
.mask_ack = mask_and_ack_8259A,
};
@ -409,12 +410,6 @@ void __init native_init_IRQ(void)
*/
intr_init_hook();
/*
* Set the clock to HZ Hz, we already have a valid
* vector now:
*/
setup_pit_timer();
/*
* External FPU? Set up irq13 if so, for
* original braindamaged IBM FERR coupling.

View File

@ -482,8 +482,8 @@ static void do_irq_balance(void)
package_index = CPU_TO_PACKAGEINDEX(i);
for (j = 0; j < NR_IRQS; j++) {
unsigned long value_now, delta;
/* Is this an active IRQ? */
if (!irq_desc[j].action)
/* Is this an active IRQ or balancing disabled ? */
if (!irq_desc[j].action || irq_balancing_disabled(j))
continue;
if ( package_index == i )
IRQ_DELTA(package_index,j) = 0;
@ -1281,11 +1281,9 @@ static void ioapic_register_intr(int irq, int vector, unsigned long trigger)
trigger == IOAPIC_LEVEL)
set_irq_chip_and_handler_name(irq, &ioapic_chip,
handle_fasteoi_irq, "fasteoi");
else {
irq_desc[irq].status |= IRQ_DELAYED_DISABLE;
else
set_irq_chip_and_handler_name(irq, &ioapic_chip,
handle_edge_irq, "edge");
}
set_intr_gate(vector, interrupt[irq]);
}
@ -1588,7 +1586,7 @@ void /*__init*/ print_local_APIC(void * dummy)
v = apic_read(APIC_LVR);
printk(KERN_INFO "... APIC VERSION: %08x\n", v);
ver = GET_APIC_VERSION(v);
maxlvt = get_maxlvt();
maxlvt = lapic_get_maxlvt();
v = apic_read(APIC_TASKPRI);
printk(KERN_DEBUG "... APIC TASKPRI: %08x (%02x)\n", v, v & APIC_TPRI_MASK);
@ -1920,7 +1918,7 @@ static void __init setup_ioapic_ids_from_mpc(void)
static void __init setup_ioapic_ids_from_mpc(void) { }
#endif
static int no_timer_check __initdata;
int no_timer_check __initdata;
static int __init notimercheck(char *s)
{
@ -2310,7 +2308,7 @@ static inline void __init check_timer(void)
disable_8259A_irq(0);
set_irq_chip_and_handler_name(0, &lapic_chip, handle_fasteoi_irq,
"fasteio");
"fasteoi");
apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */
enable_8259A_irq(0);

View File

@ -10,7 +10,6 @@
* io_apic.c.)
*/
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
@ -19,19 +18,36 @@
#include <linux/cpu.h>
#include <linux/delay.h>
#include <asm/idle.h>
#include <asm/apic.h>
#include <asm/uaccess.h>
DEFINE_PER_CPU(irq_cpustat_t, irq_stat) ____cacheline_internodealigned_in_smp;
EXPORT_PER_CPU_SYMBOL(irq_stat);
#ifndef CONFIG_X86_LOCAL_APIC
/*
* 'what should we do if we get a hw irq event on an illegal vector'.
* each architecture has to answer this themselves.
*/
void ack_bad_irq(unsigned int irq)
{
printk("unexpected IRQ trap at vector %02x\n", irq);
}
printk(KERN_ERR "unexpected IRQ trap at vector %02x\n", irq);
#ifdef CONFIG_X86_LOCAL_APIC
/*
* Currently unexpected vectors happen only on SMP and APIC.
* We _must_ ack these because every local APIC has only N
* irq slots per priority level, and a 'hanging, unacked' IRQ
* holds up an irq slot - in excessive cases (when multiple
* unexpected vectors occur) that might lock up the APIC
* completely.
* But only ack when the APIC is enabled -AK
*/
if (cpu_has_apic)
ack_APIC_irq();
#endif
}
#ifdef CONFIG_4KSTACKS
/*
@ -61,6 +77,7 @@ fastcall unsigned int do_IRQ(struct pt_regs *regs)
union irq_ctx *curctx, *irqctx;
u32 *isp;
#endif
exit_idle();
if (unlikely((unsigned)irq >= NR_IRQS)) {
printk(KERN_EMERG "%s: cannot handle IRQ %d\n",

View File

@ -363,7 +363,7 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
" pushf\n"
/* skip cs, eip, orig_eax */
" subl $12, %esp\n"
" pushl %gs\n"
" pushl %fs\n"
" pushl %ds\n"
" pushl %es\n"
" pushl %eax\n"
@ -387,7 +387,7 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
" popl %edi\n"
" popl %ebp\n"
" popl %eax\n"
/* skip eip, orig_eax, es, ds, gs */
/* skip eip, orig_eax, es, ds, fs */
" addl $20, %esp\n"
" popf\n"
" ret\n");
@ -408,7 +408,7 @@ fastcall void *__kprobes trampoline_handler(struct pt_regs *regs)
spin_lock_irqsave(&kretprobe_lock, flags);
head = kretprobe_inst_table_head(current);
/* fixup registers */
regs->xcs = __KERNEL_CS;
regs->xcs = __KERNEL_CS | get_kernel_rpl();
regs->eip = trampoline_address;
regs->orig_eax = 0xffffffff;

View File

@ -384,7 +384,7 @@ static int do_microcode_update (void)
{
long cursor = 0;
int error = 0;
void *new_mc;
void *new_mc = NULL;
int cpu;
cpumask_t old;

View File

@ -68,7 +68,6 @@ static inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx)
#ifdef CONFIG_SMP
struct msr_command {
int cpu;
int err;
u32 reg;
u32 data[2];
@ -78,16 +77,14 @@ static void msr_smp_wrmsr(void *cmd_block)
{
struct msr_command *cmd = (struct msr_command *)cmd_block;
if (cmd->cpu == smp_processor_id())
cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]);
cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]);
}
static void msr_smp_rdmsr(void *cmd_block)
{
struct msr_command *cmd = (struct msr_command *)cmd_block;
if (cmd->cpu == smp_processor_id())
cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]);
cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]);
}
static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
@ -99,12 +96,11 @@ static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
if (cpu == smp_processor_id()) {
ret = wrmsr_eio(reg, eax, edx);
} else {
cmd.cpu = cpu;
cmd.reg = reg;
cmd.data[0] = eax;
cmd.data[1] = edx;
smp_call_function(msr_smp_wrmsr, &cmd, 1, 1);
smp_call_function_single(cpu, msr_smp_wrmsr, &cmd, 1, 1);
ret = cmd.err;
}
preempt_enable();
@ -120,10 +116,9 @@ static inline int do_rdmsr(int cpu, u32 reg, u32 * eax, u32 * edx)
if (cpu == smp_processor_id()) {
ret = rdmsr_eio(reg, eax, edx);
} else {
cmd.cpu = cpu;
cmd.reg = reg;
smp_call_function(msr_smp_rdmsr, &cmd, 1, 1);
smp_call_function_single(cpu, msr_smp_rdmsr, &cmd, 1, 1);
*eax = cmd.data[0];
*edx = cmd.data[1];

View File

@ -23,6 +23,7 @@
#include <linux/dmi.h>
#include <linux/kprobes.h>
#include <linux/cpumask.h>
#include <linux/kernel_stat.h>
#include <asm/smp.h>
#include <asm/nmi.h>
@ -185,7 +186,8 @@ static __cpuinit inline int nmi_known_cpu(void)
{
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD:
return ((boot_cpu_data.x86 == 15) || (boot_cpu_data.x86 == 6));
return ((boot_cpu_data.x86 == 15) || (boot_cpu_data.x86 == 6)
|| (boot_cpu_data.x86 == 16));
case X86_VENDOR_INTEL:
if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON))
return 1;
@ -216,6 +218,28 @@ static __init void nmi_cpu_busy(void *data)
}
#endif
static unsigned int adjust_for_32bit_ctr(unsigned int hz)
{
u64 counter_val;
unsigned int retval = hz;
/*
* On Intel CPUs with P6/ARCH_PERFMON only 32 bits in the counter
* are writable, with higher bits sign extending from bit 31.
* So, we can only program the counter with 31 bit values and
* 32nd bit should be 1, for 33.. to be 1.
* Find the appropriate nmi_hz
*/
counter_val = (u64)cpu_khz * 1000;
do_div(counter_val, retval);
if (counter_val > 0x7fffffffULL) {
u64 count = (u64)cpu_khz * 1000;
do_div(count, 0x7fffffffUL);
retval = count + 1;
}
return retval;
}
static int __init check_nmi_watchdog(void)
{
unsigned int *prev_nmi_count;
@ -281,18 +305,10 @@ static int __init check_nmi_watchdog(void)
struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
nmi_hz = 1;
/*
* On Intel CPUs with ARCH_PERFMON only 32 bits in the counter
* are writable, with higher bits sign extending from bit 31.
* So, we can only program the counter with 31 bit values and
* 32nd bit should be 1, for 33.. to be 1.
* Find the appropriate nmi_hz
*/
if (wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0 &&
((u64)cpu_khz * 1000) > 0x7fffffffULL) {
u64 count = (u64)cpu_khz * 1000;
do_div(count, 0x7fffffffUL);
nmi_hz = count + 1;
if (wd->perfctr_msr == MSR_P6_PERFCTR0 ||
wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0) {
nmi_hz = adjust_for_32bit_ctr(nmi_hz);
}
}
@ -369,6 +385,34 @@ void enable_timer_nmi_watchdog(void)
}
}
static void __acpi_nmi_disable(void *__unused)
{
apic_write_around(APIC_LVT0, APIC_DM_NMI | APIC_LVT_MASKED);
}
/*
* Disable timer based NMIs on all CPUs:
*/
void acpi_nmi_disable(void)
{
if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC)
on_each_cpu(__acpi_nmi_disable, NULL, 0, 1);
}
static void __acpi_nmi_enable(void *__unused)
{
apic_write_around(APIC_LVT0, APIC_DM_NMI);
}
/*
* Enable timer based NMIs on all CPUs:
*/
void acpi_nmi_enable(void)
{
if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC)
on_each_cpu(__acpi_nmi_enable, NULL, 0, 1);
}
#ifdef CONFIG_PM
static int nmi_pm_active; /* nmi_active before suspend */
@ -442,6 +486,17 @@ static void write_watchdog_counter(unsigned int perfctr_msr, const char *descr)
wrmsrl(perfctr_msr, 0 - count);
}
static void write_watchdog_counter32(unsigned int perfctr_msr,
const char *descr)
{
u64 count = (u64)cpu_khz * 1000;
do_div(count, nmi_hz);
if(descr)
Dprintk("setting %s to -0x%08Lx\n", descr, count);
wrmsr(perfctr_msr, (u32)(-count), 0);
}
/* Note that these events don't tick when the CPU idles. This means
the frequency varies with CPU load. */
@ -531,7 +586,8 @@ static int setup_p6_watchdog(void)
/* setup the timer */
wrmsr(evntsel_msr, evntsel, 0);
write_watchdog_counter(perfctr_msr, "P6_PERFCTR0");
nmi_hz = adjust_for_32bit_ctr(nmi_hz);
write_watchdog_counter32(perfctr_msr, "P6_PERFCTR0");
apic_write(APIC_LVTPC, APIC_DM_NMI);
evntsel |= P6_EVNTSEL0_ENABLE;
wrmsr(evntsel_msr, evntsel, 0);
@ -704,7 +760,8 @@ static int setup_intel_arch_watchdog(void)
/* setup the timer */
wrmsr(evntsel_msr, evntsel, 0);
write_watchdog_counter(perfctr_msr, "INTEL_ARCH_PERFCTR0");
nmi_hz = adjust_for_32bit_ctr(nmi_hz);
write_watchdog_counter32(perfctr_msr, "INTEL_ARCH_PERFCTR0");
apic_write(APIC_LVTPC, APIC_DM_NMI);
evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE;
wrmsr(evntsel_msr, evntsel, 0);
@ -762,7 +819,8 @@ void setup_apic_nmi_watchdog (void *unused)
if (nmi_watchdog == NMI_LOCAL_APIC) {
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD:
if (boot_cpu_data.x86 != 6 && boot_cpu_data.x86 != 15)
if (boot_cpu_data.x86 != 6 && boot_cpu_data.x86 != 15 &&
boot_cpu_data.x86 != 16)
return;
if (!setup_k7_watchdog())
return;
@ -916,9 +974,13 @@ __kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
cpu_clear(cpu, backtrace_mask);
}
sum = per_cpu(irq_stat, cpu).apic_timer_irqs;
/*
* Take the local apic timer and PIT/HPET into account. We don't
* know which one is active, when we have highres/dyntick on
*/
sum = per_cpu(irq_stat, cpu).apic_timer_irqs + kstat_irqs(0);
/* if the apic timer isn't firing, this cpu isn't doing much */
/* if the none of the timers isn't firing, this cpu isn't doing much */
if (!touched && last_irq_sums[cpu] == sum) {
/*
* Ayiee, looks like this CPU is stuck ...
@ -956,6 +1018,8 @@ __kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
dummy &= ~P4_CCCR_OVF;
wrmsrl(wd->cccr_msr, dummy);
apic_write(APIC_LVTPC, APIC_DM_NMI);
/* start the cycle over again */
write_watchdog_counter(wd->perfctr_msr, NULL);
}
else if (wd->perfctr_msr == MSR_P6_PERFCTR0 ||
wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0) {
@ -964,9 +1028,12 @@ __kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
* other P6 variant.
* ArchPerfom/Core Duo also needs this */
apic_write(APIC_LVTPC, APIC_DM_NMI);
/* P6/ARCH_PERFMON has 32 bit counter write */
write_watchdog_counter32(wd->perfctr_msr, NULL);
} else {
/* start the cycle over again */
write_watchdog_counter(wd->perfctr_msr, NULL);
}
/* start the cycle over again */
write_watchdog_counter(wd->perfctr_msr, NULL);
rc = 1;
} else if (nmi_watchdog == NMI_IO_APIC) {
/* don't know how to accurately check for this.

View File

@ -92,7 +92,7 @@ static unsigned native_patch(u8 type, u16 clobbers, void *insns, unsigned len)
return insn_len;
}
static fastcall unsigned long native_get_debugreg(int regno)
static unsigned long native_get_debugreg(int regno)
{
unsigned long val = 0; /* Damn you, gcc! */
@ -115,7 +115,7 @@ static fastcall unsigned long native_get_debugreg(int regno)
return val;
}
static fastcall void native_set_debugreg(int regno, unsigned long value)
static void native_set_debugreg(int regno, unsigned long value)
{
switch (regno) {
case 0:
@ -146,55 +146,55 @@ void init_IRQ(void)
paravirt_ops.init_IRQ();
}
static fastcall void native_clts(void)
static void native_clts(void)
{
asm volatile ("clts");
}
static fastcall unsigned long native_read_cr0(void)
static unsigned long native_read_cr0(void)
{
unsigned long val;
asm volatile("movl %%cr0,%0\n\t" :"=r" (val));
return val;
}
static fastcall void native_write_cr0(unsigned long val)
static void native_write_cr0(unsigned long val)
{
asm volatile("movl %0,%%cr0": :"r" (val));
}
static fastcall unsigned long native_read_cr2(void)
static unsigned long native_read_cr2(void)
{
unsigned long val;
asm volatile("movl %%cr2,%0\n\t" :"=r" (val));
return val;
}
static fastcall void native_write_cr2(unsigned long val)
static void native_write_cr2(unsigned long val)
{
asm volatile("movl %0,%%cr2": :"r" (val));
}
static fastcall unsigned long native_read_cr3(void)
static unsigned long native_read_cr3(void)
{
unsigned long val;
asm volatile("movl %%cr3,%0\n\t" :"=r" (val));
return val;
}
static fastcall void native_write_cr3(unsigned long val)
static void native_write_cr3(unsigned long val)
{
asm volatile("movl %0,%%cr3": :"r" (val));
}
static fastcall unsigned long native_read_cr4(void)
static unsigned long native_read_cr4(void)
{
unsigned long val;
asm volatile("movl %%cr4,%0\n\t" :"=r" (val));
return val;
}
static fastcall unsigned long native_read_cr4_safe(void)
static unsigned long native_read_cr4_safe(void)
{
unsigned long val;
/* This could fault if %cr4 does not exist */
@ -207,51 +207,51 @@ static fastcall unsigned long native_read_cr4_safe(void)
return val;
}
static fastcall void native_write_cr4(unsigned long val)
static void native_write_cr4(unsigned long val)
{
asm volatile("movl %0,%%cr4": :"r" (val));
}
static fastcall unsigned long native_save_fl(void)
static unsigned long native_save_fl(void)
{
unsigned long f;
asm volatile("pushfl ; popl %0":"=g" (f): /* no input */);
return f;
}
static fastcall void native_restore_fl(unsigned long f)
static void native_restore_fl(unsigned long f)
{
asm volatile("pushl %0 ; popfl": /* no output */
:"g" (f)
:"memory", "cc");
}
static fastcall void native_irq_disable(void)
static void native_irq_disable(void)
{
asm volatile("cli": : :"memory");
}
static fastcall void native_irq_enable(void)
static void native_irq_enable(void)
{
asm volatile("sti": : :"memory");
}
static fastcall void native_safe_halt(void)
static void native_safe_halt(void)
{
asm volatile("sti; hlt": : :"memory");
}
static fastcall void native_halt(void)
static void native_halt(void)
{
asm volatile("hlt": : :"memory");
}
static fastcall void native_wbinvd(void)
static void native_wbinvd(void)
{
asm volatile("wbinvd": : :"memory");
}
static fastcall unsigned long long native_read_msr(unsigned int msr, int *err)
static unsigned long long native_read_msr(unsigned int msr, int *err)
{
unsigned long long val;
@ -270,7 +270,7 @@ static fastcall unsigned long long native_read_msr(unsigned int msr, int *err)
return val;
}
static fastcall int native_write_msr(unsigned int msr, unsigned long long val)
static int native_write_msr(unsigned int msr, unsigned long long val)
{
int err;
asm volatile("2: wrmsr ; xorl %0,%0\n"
@ -288,53 +288,53 @@ static fastcall int native_write_msr(unsigned int msr, unsigned long long val)
return err;
}
static fastcall unsigned long long native_read_tsc(void)
static unsigned long long native_read_tsc(void)
{
unsigned long long val;
asm volatile("rdtsc" : "=A" (val));
return val;
}
static fastcall unsigned long long native_read_pmc(void)
static unsigned long long native_read_pmc(void)
{
unsigned long long val;
asm volatile("rdpmc" : "=A" (val));
return val;
}
static fastcall void native_load_tr_desc(void)
static void native_load_tr_desc(void)
{
asm volatile("ltr %w0"::"q" (GDT_ENTRY_TSS*8));
}
static fastcall void native_load_gdt(const struct Xgt_desc_struct *dtr)
static void native_load_gdt(const struct Xgt_desc_struct *dtr)
{
asm volatile("lgdt %0"::"m" (*dtr));
}
static fastcall void native_load_idt(const struct Xgt_desc_struct *dtr)
static void native_load_idt(const struct Xgt_desc_struct *dtr)
{
asm volatile("lidt %0"::"m" (*dtr));
}
static fastcall void native_store_gdt(struct Xgt_desc_struct *dtr)
static void native_store_gdt(struct Xgt_desc_struct *dtr)
{
asm ("sgdt %0":"=m" (*dtr));
}
static fastcall void native_store_idt(struct Xgt_desc_struct *dtr)
static void native_store_idt(struct Xgt_desc_struct *dtr)
{
asm ("sidt %0":"=m" (*dtr));
}
static fastcall unsigned long native_store_tr(void)
static unsigned long native_store_tr(void)
{
unsigned long tr;
asm ("str %0":"=r" (tr));
return tr;
}
static fastcall void native_load_tls(struct thread_struct *t, unsigned int cpu)
static void native_load_tls(struct thread_struct *t, unsigned int cpu)
{
#define C(i) get_cpu_gdt_table(cpu)[GDT_ENTRY_TLS_MIN + i] = t->tls_array[i]
C(0); C(1); C(2);
@ -348,22 +348,22 @@ static inline void native_write_dt_entry(void *dt, int entry, u32 entry_low, u32
lp[1] = entry_high;
}
static fastcall void native_write_ldt_entry(void *dt, int entrynum, u32 low, u32 high)
static void native_write_ldt_entry(void *dt, int entrynum, u32 low, u32 high)
{
native_write_dt_entry(dt, entrynum, low, high);
}
static fastcall void native_write_gdt_entry(void *dt, int entrynum, u32 low, u32 high)
static void native_write_gdt_entry(void *dt, int entrynum, u32 low, u32 high)
{
native_write_dt_entry(dt, entrynum, low, high);
}
static fastcall void native_write_idt_entry(void *dt, int entrynum, u32 low, u32 high)
static void native_write_idt_entry(void *dt, int entrynum, u32 low, u32 high)
{
native_write_dt_entry(dt, entrynum, low, high);
}
static fastcall void native_load_esp0(struct tss_struct *tss,
static void native_load_esp0(struct tss_struct *tss,
struct thread_struct *thread)
{
tss->esp0 = thread->esp0;
@ -375,12 +375,12 @@ static fastcall void native_load_esp0(struct tss_struct *tss,
}
}
static fastcall void native_io_delay(void)
static void native_io_delay(void)
{
asm volatile("outb %al,$0x80");
}
static fastcall void native_flush_tlb(void)
static void native_flush_tlb(void)
{
__native_flush_tlb();
}
@ -389,49 +389,49 @@ static fastcall void native_flush_tlb(void)
* Global pages have to be flushed a bit differently. Not a real
* performance problem because this does not happen often.
*/
static fastcall void native_flush_tlb_global(void)
static void native_flush_tlb_global(void)
{
__native_flush_tlb_global();
}
static fastcall void native_flush_tlb_single(u32 addr)
static void native_flush_tlb_single(u32 addr)
{
__native_flush_tlb_single(addr);
}
#ifndef CONFIG_X86_PAE
static fastcall void native_set_pte(pte_t *ptep, pte_t pteval)
static void native_set_pte(pte_t *ptep, pte_t pteval)
{
*ptep = pteval;
}
static fastcall void native_set_pte_at(struct mm_struct *mm, u32 addr, pte_t *ptep, pte_t pteval)
static void native_set_pte_at(struct mm_struct *mm, u32 addr, pte_t *ptep, pte_t pteval)
{
*ptep = pteval;
}
static fastcall void native_set_pmd(pmd_t *pmdp, pmd_t pmdval)
static void native_set_pmd(pmd_t *pmdp, pmd_t pmdval)
{
*pmdp = pmdval;
}
#else /* CONFIG_X86_PAE */
static fastcall void native_set_pte(pte_t *ptep, pte_t pte)
static void native_set_pte(pte_t *ptep, pte_t pte)
{
ptep->pte_high = pte.pte_high;
smp_wmb();
ptep->pte_low = pte.pte_low;
}
static fastcall void native_set_pte_at(struct mm_struct *mm, u32 addr, pte_t *ptep, pte_t pte)
static void native_set_pte_at(struct mm_struct *mm, u32 addr, pte_t *ptep, pte_t pte)
{
ptep->pte_high = pte.pte_high;
smp_wmb();
ptep->pte_low = pte.pte_low;
}
static fastcall void native_set_pte_present(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte)
static void native_set_pte_present(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte)
{
ptep->pte_low = 0;
smp_wmb();
@ -440,29 +440,29 @@ static fastcall void native_set_pte_present(struct mm_struct *mm, unsigned long
ptep->pte_low = pte.pte_low;
}
static fastcall void native_set_pte_atomic(pte_t *ptep, pte_t pteval)
static void native_set_pte_atomic(pte_t *ptep, pte_t pteval)
{
set_64bit((unsigned long long *)ptep,pte_val(pteval));
}
static fastcall void native_set_pmd(pmd_t *pmdp, pmd_t pmdval)
static void native_set_pmd(pmd_t *pmdp, pmd_t pmdval)
{
set_64bit((unsigned long long *)pmdp,pmd_val(pmdval));
}
static fastcall void native_set_pud(pud_t *pudp, pud_t pudval)
static void native_set_pud(pud_t *pudp, pud_t pudval)
{
*pudp = pudval;
}
static fastcall void native_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
static void native_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{
ptep->pte_low = 0;
smp_wmb();
ptep->pte_high = 0;
}
static fastcall void native_pmd_clear(pmd_t *pmd)
static void native_pmd_clear(pmd_t *pmd)
{
u32 *tmp = (u32 *)pmd;
*tmp = 0;
@ -472,8 +472,8 @@ static fastcall void native_pmd_clear(pmd_t *pmd)
#endif /* CONFIG_X86_PAE */
/* These are in entry.S */
extern fastcall void native_iret(void);
extern fastcall void native_irq_enable_sysexit(void);
extern void native_iret(void);
extern void native_irq_enable_sysexit(void);
static int __init print_banner(void)
{
@ -482,9 +482,6 @@ static int __init print_banner(void)
}
core_initcall(print_banner);
/* We simply declare start_kernel to be the paravirt probe of last resort. */
paravirt_probe(start_kernel);
struct paravirt_ops paravirt_ops = {
.name = "bare hardware",
.paravirt_enabled = 0,
@ -544,12 +541,21 @@ struct paravirt_ops paravirt_ops = {
.apic_write = native_apic_write,
.apic_write_atomic = native_apic_write_atomic,
.apic_read = native_apic_read,
.setup_boot_clock = setup_boot_APIC_clock,
.setup_secondary_clock = setup_secondary_APIC_clock,
#endif
.set_lazy_mode = (void *)native_nop,
.flush_tlb_user = native_flush_tlb,
.flush_tlb_kernel = native_flush_tlb_global,
.flush_tlb_single = native_flush_tlb_single,
.alloc_pt = (void *)native_nop,
.alloc_pd = (void *)native_nop,
.alloc_pd_clone = (void *)native_nop,
.release_pt = (void *)native_nop,
.release_pd = (void *)native_nop,
.set_pte = native_set_pte,
.set_pte_at = native_set_pte_at,
.set_pmd = native_set_pmd,
@ -565,6 +571,8 @@ struct paravirt_ops paravirt_ops = {
.irq_enable_sysexit = native_irq_enable_sysexit,
.iret = native_iret,
.startup_ipi_hook = (void *)native_nop,
};
/*

View File

@ -0,0 +1,20 @@
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/init.h>
static __init int add_pcspkr(void)
{
struct platform_device *pd;
int ret;
pd = platform_device_alloc("pcspkr", -1);
if (!pd)
return -ENOMEM;
ret = platform_device_add(pd);
if (ret)
platform_device_put(pd);
return ret;
}
device_initcall(add_pcspkr);

View File

@ -38,6 +38,7 @@
#include <linux/ptrace.h>
#include <linux/random.h>
#include <linux/personality.h>
#include <linux/tick.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@ -48,6 +49,7 @@
#include <asm/i387.h>
#include <asm/desc.h>
#include <asm/vm86.h>
#include <asm/idle.h>
#ifdef CONFIG_MATH_EMULATION
#include <asm/math_emu.h>
#endif
@ -80,6 +82,42 @@ void (*pm_idle)(void);
EXPORT_SYMBOL(pm_idle);
static DEFINE_PER_CPU(unsigned int, cpu_idle_state);
static ATOMIC_NOTIFIER_HEAD(idle_notifier);
void idle_notifier_register(struct notifier_block *n)
{
atomic_notifier_chain_register(&idle_notifier, n);
}
void idle_notifier_unregister(struct notifier_block *n)
{
atomic_notifier_chain_unregister(&idle_notifier, n);
}
static DEFINE_PER_CPU(volatile unsigned long, idle_state);
void enter_idle(void)
{
/* needs to be atomic w.r.t. interrupts, not against other CPUs */
__set_bit(0, &__get_cpu_var(idle_state));
atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL);
}
static void __exit_idle(void)
{
/* needs to be atomic w.r.t. interrupts, not against other CPUs */
if (__test_and_clear_bit(0, &__get_cpu_var(idle_state)) == 0)
return;
atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL);
}
void exit_idle(void)
{
if (current->pid)
return;
__exit_idle();
}
void disable_hlt(void)
{
hlt_counter++;
@ -130,6 +168,7 @@ EXPORT_SYMBOL(default_idle);
*/
static void poll_idle (void)
{
local_irq_enable();
cpu_relax();
}
@ -173,6 +212,7 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick();
while (!need_resched()) {
void (*idle)(void);
@ -189,8 +229,18 @@ void cpu_idle(void)
play_dead();
__get_cpu_var(irq_stat).idle_timestamp = jiffies;
/*
* Idle routines should keep interrupts disabled
* from here on, until they go to idle.
* Otherwise, idle callbacks can misfire.
*/
local_irq_disable();
enter_idle();
idle();
__exit_idle();
}
tick_nohz_restart_sched_tick();
preempt_enable_no_resched();
schedule();
preempt_disable();
@ -243,7 +293,11 @@ void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
__monitor((void *)&current_thread_info()->flags, 0, 0);
smp_mb();
if (!need_resched())
__mwait(eax, ecx);
__sti_mwait(eax, ecx);
else
local_irq_enable();
} else {
local_irq_enable();
}
}
@ -308,8 +362,8 @@ void show_regs(struct pt_regs * regs)
regs->eax,regs->ebx,regs->ecx,regs->edx);
printk("ESI: %08lx EDI: %08lx EBP: %08lx",
regs->esi, regs->edi, regs->ebp);
printk(" DS: %04x ES: %04x GS: %04x\n",
0xffff & regs->xds,0xffff & regs->xes, 0xffff & regs->xgs);
printk(" DS: %04x ES: %04x FS: %04x\n",
0xffff & regs->xds,0xffff & regs->xes, 0xffff & regs->xfs);
cr0 = read_cr0();
cr2 = read_cr2();
@ -340,7 +394,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
regs.xds = __USER_DS;
regs.xes = __USER_DS;
regs.xgs = __KERNEL_PDA;
regs.xfs = __KERNEL_PDA;
regs.orig_eax = -1;
regs.eip = (unsigned long) kernel_thread_helper;
regs.xcs = __KERNEL_CS | get_kernel_rpl();
@ -425,7 +479,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
p->thread.eip = (unsigned long) ret_from_fork;
savesegment(fs,p->thread.fs);
savesegment(gs,p->thread.gs);
tsk = current;
if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) {
@ -501,8 +555,8 @@ void dump_thread(struct pt_regs * regs, struct user * dump)
dump->regs.eax = regs->eax;
dump->regs.ds = regs->xds;
dump->regs.es = regs->xes;
savesegment(fs,dump->regs.fs);
dump->regs.gs = regs->xgs;
dump->regs.fs = regs->xfs;
savesegment(gs,dump->regs.gs);
dump->regs.orig_eax = regs->orig_eax;
dump->regs.eip = regs->eip;
dump->regs.cs = regs->xcs;
@ -653,7 +707,7 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas
load_esp0(tss, next);
/*
* Save away %fs. No need to save %gs, as it was saved on the
* Save away %gs. No need to save %fs, as it was saved on the
* stack on entry. No need to save %es and %ds, as those are
* always kernel segments while inside the kernel. Doing this
* before setting the new TLS descriptors avoids the situation
@ -662,7 +716,7 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas
* used %fs or %gs (it does not today), or if the kernel is
* running inside of a hypervisor layer.
*/
savesegment(fs, prev->fs);
savesegment(gs, prev->gs);
/*
* Load the per-thread Thread-Local Storage descriptor.
@ -670,14 +724,13 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas
load_TLS(next, cpu);
/*
* Restore %fs if needed.
*
* Glibc normally makes %fs be zero.
* Restore IOPL if needed. In normal use, the flags restore
* in the switch assembly will handle this. But if the kernel
* is running virtualized at a non-zero CPL, the popf will
* not restore flags, so it must be done in a separate step.
*/
if (unlikely(prev->fs | next->fs))
loadsegment(fs, next->fs);
write_pda(pcurrent, next_p);
if (get_kernel_rpl() && unlikely(prev->iopl != next->iopl))
set_iopl_mask(next->iopl);
/*
* Now maybe handle debug registers and/or IO bitmaps
@ -688,6 +741,15 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas
disable_tsc(prev_p, next_p);
/*
* Leave lazy mode, flushing any hypercalls made here.
* This must be done before restoring TLS segments so
* the GDT and LDT are properly updated, and must be
* done before math_state_restore, so the TS bit is up
* to date.
*/
arch_leave_lazy_cpu_mode();
/* If the task has used fpu the last 5 timeslices, just do a full
* restore of the math state immediately to avoid the trap; the
* chances of needing FPU soon are obviously high now
@ -695,6 +757,14 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas
if (next_p->fpu_counter > 5)
math_state_restore();
/*
* Restore %gs if needed (which is common)
*/
if (prev->gs | next->gs)
loadsegment(gs, next->gs);
write_pda(pcurrent, next_p);
return prev_p;
}

View File

@ -89,14 +89,14 @@ static int putreg(struct task_struct *child,
unsigned long regno, unsigned long value)
{
switch (regno >> 2) {
case FS:
case GS:
if (value && (value & 3) != 3)
return -EIO;
child->thread.fs = value;
child->thread.gs = value;
return 0;
case DS:
case ES:
case GS:
case FS:
if (value && (value & 3) != 3)
return -EIO;
value &= 0xffff;
@ -112,7 +112,7 @@ static int putreg(struct task_struct *child,
value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
break;
}
if (regno > ES*4)
if (regno > FS*4)
regno -= 1*4;
put_stack_long(child, regno, value);
return 0;
@ -124,18 +124,18 @@ static unsigned long getreg(struct task_struct *child,
unsigned long retval = ~0UL;
switch (regno >> 2) {
case FS:
retval = child->thread.fs;
case GS:
retval = child->thread.gs;
break;
case DS:
case ES:
case GS:
case FS:
case SS:
case CS:
retval = 0xffff;
/* fall through */
default:
if (regno > ES*4)
if (regno > FS*4)
regno -= 1*4;
retval &= get_stack_long(child, regno);
}

View File

@ -33,7 +33,6 @@
#include <linux/initrd.h>
#include <linux/bootmem.h>
#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/console.h>
#include <linux/mca.h>
#include <linux/root_dev.h>
@ -60,6 +59,7 @@
#include <asm/io_apic.h>
#include <asm/ist.h>
#include <asm/io.h>
#include <asm/vmi.h>
#include <setup_arch.h>
#include <bios_ebda.h>
@ -581,6 +581,14 @@ void __init setup_arch(char **cmdline_p)
max_low_pfn = setup_memory();
#ifdef CONFIG_VMI
/*
* Must be after max_low_pfn is determined, and before kernel
* pagetables are setup.
*/
vmi_init();
#endif
/*
* NOTE: before this point _nobody_ is allowed to allocate
* any memory using the bootmem allocator. Although the
@ -651,28 +659,3 @@ void __init setup_arch(char **cmdline_p)
#endif
tsc_init();
}
static __init int add_pcspkr(void)
{
struct platform_device *pd;
int ret;
pd = platform_device_alloc("pcspkr", -1);
if (!pd)
return -ENOMEM;
ret = platform_device_add(pd);
if (ret)
platform_device_put(pd);
return ret;
}
device_initcall(add_pcspkr);
/*
* Local Variables:
* mode:c
* c-file-style:"k&r"
* c-basic-offset:8
* End:
*/

View File

@ -21,6 +21,7 @@
#include <linux/suspend.h>
#include <linux/ptrace.h>
#include <linux/elf.h>
#include <linux/binfmts.h>
#include <asm/processor.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
@ -128,8 +129,8 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, int *peax
X86_EFLAGS_TF | X86_EFLAGS_SF | X86_EFLAGS_ZF | \
X86_EFLAGS_AF | X86_EFLAGS_PF | X86_EFLAGS_CF)
COPY_SEG(gs);
GET_SEG(fs);
GET_SEG(gs);
COPY_SEG(fs);
COPY_SEG(es);
COPY_SEG(ds);
COPY(edi);
@ -244,9 +245,9 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
{
int tmp, err = 0;
err |= __put_user(regs->xgs, (unsigned int __user *)&sc->gs);
savesegment(fs, tmp);
err |= __put_user(tmp, (unsigned int __user *)&sc->fs);
err |= __put_user(regs->xfs, (unsigned int __user *)&sc->fs);
savesegment(gs, tmp);
err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
err |= __put_user(regs->xes, (unsigned int __user *)&sc->es);
err |= __put_user(regs->xds, (unsigned int __user *)&sc->ds);
@ -349,7 +350,10 @@ static int setup_frame(int sig, struct k_sigaction *ka,
goto give_sigsegv;
}
restorer = (void *)VDSO_SYM(&__kernel_sigreturn);
if (current->binfmt->hasvdso)
restorer = (void *)VDSO_SYM(&__kernel_sigreturn);
else
restorer = (void *)&frame->retcode;
if (ka->sa.sa_flags & SA_RESTORER)
restorer = ka->sa.sa_restorer;

View File

@ -23,6 +23,7 @@
#include <asm/mtrr.h>
#include <asm/tlbflush.h>
#include <asm/idle.h>
#include <mach_apic.h>
/*
@ -374,8 +375,7 @@ static void flush_tlb_others(cpumask_t cpumask, struct mm_struct *mm,
/*
* i'm not happy about this global shared spinlock in the
* MM hot path, but we'll see how contended it is.
* Temporarily this turns IRQs off, so that lockups are
* detected by the NMI watchdog.
* AK: x86-64 has a faster method that could be ported.
*/
spin_lock(&tlbstate_lock);
@ -400,7 +400,7 @@ static void flush_tlb_others(cpumask_t cpumask, struct mm_struct *mm,
while (!cpus_empty(flush_cpumask))
/* nothing. lockup detection does not belong here */
mb();
cpu_relax();
flush_mm = NULL;
flush_va = 0;
@ -624,6 +624,7 @@ fastcall void smp_call_function_interrupt(struct pt_regs *regs)
/*
* At this point the info structure may be out of scope unless wait==1
*/
exit_idle();
irq_enter();
(*func)(info);
irq_exit();

View File

@ -63,6 +63,7 @@
#include <mach_apic.h>
#include <mach_wakecpu.h>
#include <smpboot_hooks.h>
#include <asm/vmi.h>
/* Set if we find a B stepping CPU */
static int __devinitdata smp_b_stepping;
@ -93,12 +94,6 @@ cpumask_t cpu_possible_map;
EXPORT_SYMBOL(cpu_possible_map);
static cpumask_t smp_commenced_mask;
/* TSC's upper 32 bits can't be written in eariler CPU (before prescott), there
* is no way to resync one AP against BP. TBD: for prescott and above, we
* should use IA64's algorithm
*/
static int __devinitdata tsc_sync_disabled;
/* Per CPU bogomips and other parameters */
struct cpuinfo_x86 cpu_data[NR_CPUS] __cacheline_aligned;
EXPORT_SYMBOL(cpu_data);
@ -215,151 +210,6 @@ static void __cpuinit smp_store_cpu_info(int id)
;
}
/*
* TSC synchronization.
*
* We first check whether all CPUs have their TSC's synchronized,
* then we print a warning if not, and always resync.
*/
static struct {
atomic_t start_flag;
atomic_t count_start;
atomic_t count_stop;
unsigned long long values[NR_CPUS];
} tsc __cpuinitdata = {
.start_flag = ATOMIC_INIT(0),
.count_start = ATOMIC_INIT(0),
.count_stop = ATOMIC_INIT(0),
};
#define NR_LOOPS 5
static void __init synchronize_tsc_bp(void)
{
int i;
unsigned long long t0;
unsigned long long sum, avg;
long long delta;
unsigned int one_usec;
int buggy = 0;
printk(KERN_INFO "checking TSC synchronization across %u CPUs: ", num_booting_cpus());
/* convert from kcyc/sec to cyc/usec */
one_usec = cpu_khz / 1000;
atomic_set(&tsc.start_flag, 1);
wmb();
/*
* We loop a few times to get a primed instruction cache,
* then the last pass is more or less synchronized and
* the BP and APs set their cycle counters to zero all at
* once. This reduces the chance of having random offsets
* between the processors, and guarantees that the maximum
* delay between the cycle counters is never bigger than
* the latency of information-passing (cachelines) between
* two CPUs.
*/
for (i = 0; i < NR_LOOPS; i++) {
/*
* all APs synchronize but they loop on '== num_cpus'
*/
while (atomic_read(&tsc.count_start) != num_booting_cpus()-1)
cpu_relax();
atomic_set(&tsc.count_stop, 0);
wmb();
/*
* this lets the APs save their current TSC:
*/
atomic_inc(&tsc.count_start);
rdtscll(tsc.values[smp_processor_id()]);
/*
* We clear the TSC in the last loop:
*/
if (i == NR_LOOPS-1)
write_tsc(0, 0);
/*
* Wait for all APs to leave the synchronization point:
*/
while (atomic_read(&tsc.count_stop) != num_booting_cpus()-1)
cpu_relax();
atomic_set(&tsc.count_start, 0);
wmb();
atomic_inc(&tsc.count_stop);
}
sum = 0;
for (i = 0; i < NR_CPUS; i++) {
if (cpu_isset(i, cpu_callout_map)) {
t0 = tsc.values[i];
sum += t0;
}
}
avg = sum;
do_div(avg, num_booting_cpus());
for (i = 0; i < NR_CPUS; i++) {
if (!cpu_isset(i, cpu_callout_map))
continue;
delta = tsc.values[i] - avg;
if (delta < 0)
delta = -delta;
/*
* We report bigger than 2 microseconds clock differences.
*/
if (delta > 2*one_usec) {
long long realdelta;
if (!buggy) {
buggy = 1;
printk("\n");
}
realdelta = delta;
do_div(realdelta, one_usec);
if (tsc.values[i] < avg)
realdelta = -realdelta;
if (realdelta)
printk(KERN_INFO "CPU#%d had %Ld usecs TSC "
"skew, fixed it up.\n", i, realdelta);
}
}
if (!buggy)
printk("passed.\n");
}
static void __cpuinit synchronize_tsc_ap(void)
{
int i;
/*
* Not every cpu is online at the time
* this gets called, so we first wait for the BP to
* finish SMP initialization:
*/
while (!atomic_read(&tsc.start_flag))
cpu_relax();
for (i = 0; i < NR_LOOPS; i++) {
atomic_inc(&tsc.count_start);
while (atomic_read(&tsc.count_start) != num_booting_cpus())
cpu_relax();
rdtscll(tsc.values[smp_processor_id()]);
if (i == NR_LOOPS-1)
write_tsc(0, 0);
atomic_inc(&tsc.count_stop);
while (atomic_read(&tsc.count_stop) != num_booting_cpus())
cpu_relax();
}
}
#undef NR_LOOPS
extern void calibrate_delay(void);
static atomic_t init_deasserted;
@ -437,20 +287,12 @@ static void __cpuinit smp_callin(void)
/*
* Save our processor parameters
*/
smp_store_cpu_info(cpuid);
disable_APIC_timer();
smp_store_cpu_info(cpuid);
/*
* Allow the master to continue.
*/
cpu_set(cpuid, cpu_callin_map);
/*
* Synchronize the TSC with the BP
*/
if (cpu_has_tsc && cpu_khz && !tsc_sync_disabled)
synchronize_tsc_ap();
}
static int cpucount;
@ -545,18 +387,25 @@ static void __cpuinit start_secondary(void *unused)
* booting is too fragile that we want to limit the
* things done here to the most necessary things.
*/
#ifdef CONFIG_VMI
vmi_bringup();
#endif
secondary_cpu_init();
preempt_disable();
smp_callin();
while (!cpu_isset(smp_processor_id(), smp_commenced_mask))
rep_nop();
setup_secondary_APIC_clock();
/*
* Check TSC synchronization with the BP:
*/
check_tsc_sync_target();
setup_secondary_clock();
if (nmi_watchdog == NMI_IO_APIC) {
disable_8259A_irq(0);
enable_NMI_through_LVT0(NULL);
enable_8259A_irq(0);
}
enable_APIC_timer();
/*
* low-memory mappings have been cleared, flush them from
* the local TLBs too.
@ -619,7 +468,6 @@ extern struct {
unsigned short ss;
} stack_start;
extern struct i386_pda *start_pda;
extern struct Xgt_desc_struct cpu_gdt_descr;
#ifdef CONFIG_NUMA
@ -749,7 +597,7 @@ wakeup_secondary_cpu(int logical_apicid, unsigned long start_eip)
/*
* Due to the Pentium erratum 3AP.
*/
maxlvt = get_maxlvt();
maxlvt = lapic_get_maxlvt();
if (maxlvt > 3) {
apic_read_around(APIC_SPIV);
apic_write(APIC_ESR, 0);
@ -834,12 +682,19 @@ wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip)
else
num_starts = 0;
/*
* Paravirt / VMI wants a startup IPI hook here to set up the
* target processor state.
*/
startup_ipi_hook(phys_apicid, (unsigned long) start_secondary,
(unsigned long) stack_start.esp);
/*
* Run STARTUP IPI loop.
*/
Dprintk("#startup loops: %d.\n", num_starts);
maxlvt = get_maxlvt();
maxlvt = lapic_get_maxlvt();
for (j = 1; j <= num_starts; j++) {
Dprintk("Sending STARTUP #%d.\n",j);
@ -1115,8 +970,6 @@ static int __cpuinit __smp_prepare_cpu(int cpu)
info.cpu = cpu;
INIT_WORK(&info.task, do_warm_boot_cpu);
tsc_sync_disabled = 1;
/* init low mem mapping */
clone_pgd_range(swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
min_t(unsigned long, KERNEL_PGD_PTRS, USER_PGD_PTRS));
@ -1124,7 +977,6 @@ static int __cpuinit __smp_prepare_cpu(int cpu)
schedule_work(&info.task);
wait_for_completion(&done);
tsc_sync_disabled = 0;
zap_low_mappings();
ret = 0;
exit:
@ -1320,13 +1172,7 @@ static void __init smp_boot_cpus(unsigned int max_cpus)
smpboot_setup_io_apic();
setup_boot_APIC_clock();
/*
* Synchronize the TSC with the AP
*/
if (cpu_has_tsc && cpucount && cpu_khz)
synchronize_tsc_bp();
setup_boot_clock();
}
/* These are wrappers to interface to the new boot process. Someone
@ -1461,9 +1307,16 @@ int __cpuinit __cpu_up(unsigned int cpu)
}
local_irq_enable();
per_cpu(cpu_state, cpu) = CPU_UP_PREPARE;
/* Unleash the CPU! */
cpu_set(cpu, smp_commenced_mask);
/*
* Check TSC synchronization with the AP:
*/
check_tsc_sync_source(cpu);
while (!cpu_isset(cpu, cpu_online_map))
cpu_relax();

View File

@ -78,7 +78,7 @@ int __init sysenter_setup(void)
syscall_pages[0] = virt_to_page(syscall_page);
#ifdef CONFIG_COMPAT_VDSO
__set_fixmap(FIX_VDSO, __pa(syscall_page), PAGE_READONLY);
__set_fixmap(FIX_VDSO, __pa(syscall_page), PAGE_READONLY_EXEC);
printk("Compat vDSO mapped to %08lx.\n", __fix_to_virt(FIX_VDSO));
#endif

View File

@ -131,15 +131,13 @@ unsigned long profile_pc(struct pt_regs *regs)
unsigned long pc = instruction_pointer(regs);
#ifdef CONFIG_SMP
if (!user_mode_vm(regs) && in_lock_functions(pc)) {
if (!v8086_mode(regs) && SEGMENT_IS_KERNEL_CODE(regs->xcs) &&
in_lock_functions(pc)) {
#ifdef CONFIG_FRAME_POINTER
return *(unsigned long *)(regs->ebp + 4);
#else
unsigned long *sp;
if ((regs->xcs & 3) == 0)
sp = (unsigned long *)&regs->esp;
else
sp = (unsigned long *)regs->esp;
unsigned long *sp = (unsigned long *)&regs->esp;
/* Return address is either directly at stack pointer
or above a saved eflags. Eflags has bits 22-31 zero,
kernel addresses don't. */
@ -161,15 +159,6 @@ EXPORT_SYMBOL(profile_pc);
*/
irqreturn_t timer_interrupt(int irq, void *dev_id)
{
/*
* Here we are in the timer irq handler. We just have irqs locally
* disabled but we don't know if the timer_bh is running on the other
* CPU. We need to avoid to SMP race with it. NOTE: we don' t need
* the irq version of write_lock because as just said we have irq
* locally disabled. -arca
*/
write_seqlock(&xtime_lock);
#ifdef CONFIG_X86_IO_APIC
if (timer_ack) {
/*
@ -188,7 +177,6 @@ irqreturn_t timer_interrupt(int irq, void *dev_id)
do_timer_interrupt_hook();
if (MCA_bus) {
/* The PS/2 uses level-triggered interrupts. You can't
turn them off, nor would you want to (any attempt to
@ -203,18 +191,11 @@ irqreturn_t timer_interrupt(int irq, void *dev_id)
outb_p( irq_v|0x80, 0x61 ); /* reset the IRQ */
}
write_sequnlock(&xtime_lock);
#ifdef CONFIG_X86_LOCAL_APIC
if (using_apic_timer)
smp_send_timer_broadcast_ipi();
#endif
return IRQ_HANDLED;
}
/* not static: needed by APM */
unsigned long get_cmos_time(void)
unsigned long read_persistent_clock(void)
{
unsigned long retval;
unsigned long flags;
@ -227,11 +208,11 @@ unsigned long get_cmos_time(void)
return retval;
}
EXPORT_SYMBOL(get_cmos_time);
static void sync_cmos_clock(unsigned long dummy);
static DEFINE_TIMER(sync_cmos_timer, sync_cmos_clock, 0, 0);
int no_sync_cmos_clock;
static void sync_cmos_clock(unsigned long dummy)
{
@ -275,117 +256,20 @@ static void sync_cmos_clock(unsigned long dummy)
void notify_arch_cmos_timer(void)
{
mod_timer(&sync_cmos_timer, jiffies + 1);
if (!no_sync_cmos_clock)
mod_timer(&sync_cmos_timer, jiffies + 1);
}
static long clock_cmos_diff;
static unsigned long sleep_start;
static int timer_suspend(struct sys_device *dev, pm_message_t state)
{
/*
* Estimate time zone so that set_time can update the clock
*/
unsigned long ctime = get_cmos_time();
clock_cmos_diff = -ctime;
clock_cmos_diff += get_seconds();
sleep_start = ctime;
return 0;
}
static int timer_resume(struct sys_device *dev)
{
unsigned long flags;
unsigned long sec;
unsigned long ctime = get_cmos_time();
long sleep_length = (ctime - sleep_start) * HZ;
struct timespec ts;
if (sleep_length < 0) {
printk(KERN_WARNING "CMOS clock skew detected in timer resume!\n");
/* The time after the resume must not be earlier than the time
* before the suspend or some nasty things will happen
*/
sleep_length = 0;
ctime = sleep_start;
}
#ifdef CONFIG_HPET_TIMER
if (is_hpet_enabled())
hpet_reenable();
#endif
setup_pit_timer();
sec = ctime + clock_cmos_diff;
ts.tv_sec = sec;
ts.tv_nsec = 0;
do_settimeofday(&ts);
write_seqlock_irqsave(&xtime_lock, flags);
jiffies_64 += sleep_length;
write_sequnlock_irqrestore(&xtime_lock, flags);
touch_softlockup_watchdog();
return 0;
}
static struct sysdev_class timer_sysclass = {
.resume = timer_resume,
.suspend = timer_suspend,
set_kset_name("timer"),
};
/* XXX this driverfs stuff should probably go elsewhere later -john */
static struct sys_device device_timer = {
.id = 0,
.cls = &timer_sysclass,
};
static int time_init_device(void)
{
int error = sysdev_class_register(&timer_sysclass);
if (!error)
error = sysdev_register(&device_timer);
return error;
}
device_initcall(time_init_device);
#ifdef CONFIG_HPET_TIMER
extern void (*late_time_init)(void);
/* Duplicate of time_init() below, with hpet_enable part added */
static void __init hpet_time_init(void)
{
struct timespec ts;
ts.tv_sec = get_cmos_time();
ts.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
do_settimeofday(&ts);
if ((hpet_enable() >= 0) && hpet_use_timer) {
printk("Using HPET for base-timer\n");
}
if (!hpet_enable())
setup_pit_timer();
do_time_init();
}
#endif
void __init time_init(void)
{
struct timespec ts;
#ifdef CONFIG_HPET_TIMER
if (is_hpet_capable()) {
/*
* HPET initialization needs to do memory-mapped io. So, let
* us do a late initialization after mem_init().
*/
late_time_init = hpet_time_init;
return;
}
#endif
ts.tv_sec = get_cmos_time();
ts.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
do_settimeofday(&ts);
do_time_init();
late_time_init = hpet_time_init;
}

View File

@ -94,6 +94,7 @@ asmlinkage void spurious_interrupt_bug(void);
asmlinkage void machine_check(void);
int kstack_depth_to_print = 24;
static unsigned int code_bytes = 64;
ATOMIC_NOTIFIER_HEAD(i386die_chain);
int register_die_notifier(struct notifier_block *nb)
@ -291,10 +292,11 @@ void show_registers(struct pt_regs *regs)
int i;
int in_kernel = 1;
unsigned long esp;
unsigned short ss;
unsigned short ss, gs;
esp = (unsigned long) (&regs->esp);
savesegment(ss, ss);
savesegment(gs, gs);
if (user_mode_vm(regs)) {
in_kernel = 0;
esp = regs->esp;
@ -313,8 +315,8 @@ void show_registers(struct pt_regs *regs)
regs->eax, regs->ebx, regs->ecx, regs->edx);
printk(KERN_EMERG "esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n",
regs->esi, regs->edi, regs->ebp, esp);
printk(KERN_EMERG "ds: %04x es: %04x ss: %04x\n",
regs->xds & 0xffff, regs->xes & 0xffff, ss);
printk(KERN_EMERG "ds: %04x es: %04x fs: %04x gs: %04x ss: %04x\n",
regs->xds & 0xffff, regs->xes & 0xffff, regs->xfs & 0xffff, gs, ss);
printk(KERN_EMERG "Process %.*s (pid: %d, ti=%p task=%p task.ti=%p)",
TASK_COMM_LEN, current->comm, current->pid,
current_thread_info(), current, current->thread_info);
@ -324,7 +326,8 @@ void show_registers(struct pt_regs *regs)
*/
if (in_kernel) {
u8 *eip;
int code_bytes = 64;
unsigned int code_prologue = code_bytes * 43 / 64;
unsigned int code_len = code_bytes;
unsigned char c;
printk("\n" KERN_EMERG "Stack: ");
@ -332,14 +335,14 @@ void show_registers(struct pt_regs *regs)
printk(KERN_EMERG "Code: ");
eip = (u8 *)regs->eip - 43;
eip = (u8 *)regs->eip - code_prologue;
if (eip < (u8 *)PAGE_OFFSET ||
probe_kernel_address(eip, c)) {
/* try starting at EIP */
eip = (u8 *)regs->eip;
code_bytes = 32;
code_len = code_len - code_prologue + 1;
}
for (i = 0; i < code_bytes; i++, eip++) {
for (i = 0; i < code_len; i++, eip++) {
if (eip < (u8 *)PAGE_OFFSET ||
probe_kernel_address(eip, c)) {
printk(" Bad EIP value.");
@ -1191,3 +1194,13 @@ static int __init kstack_setup(char *s)
return 1;
}
__setup("kstack=", kstack_setup);
static int __init code_bytes_setup(char *s)
{
code_bytes = simple_strtoul(s, NULL, 0);
if (code_bytes > 8192)
code_bytes = 8192;
return 1;
}
__setup("code_bytes=", code_bytes_setup);

View File

@ -23,6 +23,7 @@
* an extra value to store the TSC freq
*/
unsigned int tsc_khz;
unsigned long long (*custom_sched_clock)(void);
int tsc_disable;
@ -59,12 +60,6 @@ static inline int check_tsc_unstable(void)
return tsc_unstable;
}
void mark_tsc_unstable(void)
{
tsc_unstable = 1;
}
EXPORT_SYMBOL_GPL(mark_tsc_unstable);
/* Accellerators for sched_clock()
* convert from cycles(64bits) => nanoseconds (64bits)
* basic equation:
@ -107,14 +102,14 @@ unsigned long long sched_clock(void)
{
unsigned long long this_offset;
if (unlikely(custom_sched_clock))
return (*custom_sched_clock)();
/*
* in the NUMA case we dont use the TSC as they are not
* synchronized across all CPUs.
* Fall back to jiffies if there's no TSC available:
*/
#ifndef CONFIG_NUMA
if (!cpu_khz || check_tsc_unstable())
#endif
/* no locking but a rare wrong value is not a big deal */
if (unlikely(tsc_disable))
/* No locking but a rare wrong value is not a big deal: */
return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ);
/* read the Time Stamp Counter: */
@ -194,13 +189,13 @@ EXPORT_SYMBOL(recalibrate_cpu_khz);
void __init tsc_init(void)
{
if (!cpu_has_tsc || tsc_disable)
return;
goto out_no_tsc;
cpu_khz = calculate_cpu_khz();
tsc_khz = cpu_khz;
if (!cpu_khz)
return;
goto out_no_tsc;
printk("Detected %lu.%03lu MHz processor.\n",
(unsigned long)cpu_khz / 1000,
@ -208,38 +203,19 @@ void __init tsc_init(void)
set_cyc2ns_scale(cpu_khz);
use_tsc_delay();
return;
out_no_tsc:
/*
* Set the tsc_disable flag if there's no TSC support, this
* makes it a fast flag for the kernel to see whether it
* should be using the TSC.
*/
tsc_disable = 1;
}
#ifdef CONFIG_CPU_FREQ
static unsigned int cpufreq_delayed_issched = 0;
static unsigned int cpufreq_init = 0;
static struct work_struct cpufreq_delayed_get_work;
static void handle_cpufreq_delayed_get(struct work_struct *work)
{
unsigned int cpu;
for_each_online_cpu(cpu)
cpufreq_get(cpu);
cpufreq_delayed_issched = 0;
}
/*
* if we notice cpufreq oddness, schedule a call to cpufreq_get() as it tries
* to verify the CPU frequency the timing core thinks the CPU is running
* at is still correct.
*/
static inline void cpufreq_delayed_get(void)
{
if (cpufreq_init && !cpufreq_delayed_issched) {
cpufreq_delayed_issched = 1;
printk(KERN_DEBUG "Checking if CPU frequency changed.\n");
schedule_work(&cpufreq_delayed_get_work);
}
}
/*
* if the CPU frequency is scaled, TSC-based delays will need a different
* loops_per_jiffy value to function properly.
@ -303,17 +279,9 @@ static struct notifier_block time_cpufreq_notifier_block = {
static int __init cpufreq_tsc(void)
{
int ret;
INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get);
ret = cpufreq_register_notifier(&time_cpufreq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
if (!ret)
cpufreq_init = 1;
return ret;
return cpufreq_register_notifier(&time_cpufreq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
}
core_initcall(cpufreq_tsc);
#endif
@ -321,7 +289,6 @@ core_initcall(cpufreq_tsc);
/* clock source code */
static unsigned long current_tsc_khz = 0;
static int tsc_update_callback(void);
static cycle_t read_tsc(void)
{
@ -339,37 +306,28 @@ static struct clocksource clocksource_tsc = {
.mask = CLOCKSOURCE_MASK(64),
.mult = 0, /* to be set */
.shift = 22,
.update_callback = tsc_update_callback,
.is_continuous = 1,
.flags = CLOCK_SOURCE_IS_CONTINUOUS |
CLOCK_SOURCE_MUST_VERIFY,
};
static int tsc_update_callback(void)
void mark_tsc_unstable(void)
{
int change = 0;
/* check to see if we should switch to the safe clocksource: */
if (clocksource_tsc.rating != 0 && check_tsc_unstable()) {
clocksource_tsc.rating = 0;
clocksource_reselect();
change = 1;
if (!tsc_unstable) {
tsc_unstable = 1;
/* Can be called before registration */
if (clocksource_tsc.mult)
clocksource_change_rating(&clocksource_tsc, 0);
else
clocksource_tsc.rating = 0;
}
/* only update if tsc_khz has changed: */
if (current_tsc_khz != tsc_khz) {
current_tsc_khz = tsc_khz;
clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,
clocksource_tsc.shift);
change = 1;
}
return change;
}
EXPORT_SYMBOL_GPL(mark_tsc_unstable);
static int __init dmi_mark_tsc_unstable(struct dmi_system_id *d)
{
printk(KERN_NOTICE "%s detected: marking TSC unstable.\n",
d->ident);
mark_tsc_unstable();
tsc_unstable = 1;
return 0;
}
@ -386,66 +344,45 @@ static struct dmi_system_id __initdata bad_tsc_dmi_table[] = {
{}
};
#define TSC_FREQ_CHECK_INTERVAL (10*MSEC_PER_SEC) /* 10sec in MS */
static struct timer_list verify_tsc_freq_timer;
/* XXX - Probably should add locking */
static void verify_tsc_freq(unsigned long unused)
{
static u64 last_tsc;
static unsigned long last_jiffies;
u64 now_tsc, interval_tsc;
unsigned long now_jiffies, interval_jiffies;
if (check_tsc_unstable())
return;
rdtscll(now_tsc);
now_jiffies = jiffies;
if (!last_jiffies) {
goto out;
}
interval_jiffies = now_jiffies - last_jiffies;
interval_tsc = now_tsc - last_tsc;
interval_tsc *= HZ;
do_div(interval_tsc, cpu_khz*1000);
if (interval_tsc < (interval_jiffies * 3 / 4)) {
printk("TSC appears to be running slowly. "
"Marking it as unstable\n");
mark_tsc_unstable();
return;
}
out:
last_tsc = now_tsc;
last_jiffies = now_jiffies;
/* set us up to go off on the next interval: */
mod_timer(&verify_tsc_freq_timer,
jiffies + msecs_to_jiffies(TSC_FREQ_CHECK_INTERVAL));
}
/*
* Make an educated guess if the TSC is trustworthy and synchronized
* over all CPUs.
*/
static __init int unsynchronized_tsc(void)
__cpuinit int unsynchronized_tsc(void)
{
if (!cpu_has_tsc || tsc_unstable)
return 1;
/*
* Intel systems are normally all synchronized.
* Exceptions must mark TSC as unstable:
*/
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
return 0;
/* assume multi socket systems are not synchronized: */
return num_possible_cpus() > 1;
if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) {
/* assume multi socket systems are not synchronized: */
if (num_possible_cpus() > 1)
tsc_unstable = 1;
}
return tsc_unstable;
}
/*
* Geode_LX - the OLPC CPU has a possibly a very reliable TSC
*/
#ifdef CONFIG_MGEODE_LX
/* RTSC counts during suspend */
#define RTSC_SUSP 0x100
static void __init check_geode_tsc_reliable(void)
{
unsigned long val;
rdmsrl(MSR_GEODE_BUSCONT_CONF0, val);
if ((val & RTSC_SUSP))
clocksource_tsc.flags &= ~CLOCK_SOURCE_MUST_VERIFY;
}
#else
static inline void check_geode_tsc_reliable(void) { }
#endif
static int __init init_tsc_clocksource(void)
{
@ -453,20 +390,16 @@ static int __init init_tsc_clocksource(void)
/* check blacklist */
dmi_check_system(bad_tsc_dmi_table);
if (unsynchronized_tsc()) /* mark unstable if unsynced */
mark_tsc_unstable();
unsynchronized_tsc();
check_geode_tsc_reliable();
current_tsc_khz = tsc_khz;
clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,
clocksource_tsc.shift);
/* lower the rating if we already know its unstable: */
if (check_tsc_unstable())
if (check_tsc_unstable()) {
clocksource_tsc.rating = 0;
init_timer(&verify_tsc_freq_timer);
verify_tsc_freq_timer.function = verify_tsc_freq;
verify_tsc_freq_timer.expires =
jiffies + msecs_to_jiffies(TSC_FREQ_CHECK_INTERVAL);
add_timer(&verify_tsc_freq_timer);
clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS;
}
return clocksource_register(&clocksource_tsc);
}

View File

@ -0,0 +1 @@
#include "../../x86_64/kernel/tsc_sync.c"

View File

@ -96,12 +96,12 @@ static int copy_vm86_regs_to_user(struct vm86_regs __user *user,
{
int ret = 0;
/* kernel_vm86_regs is missing xfs, so copy everything up to
(but not including) xgs, and then rest after xgs. */
ret += copy_to_user(user, regs, offsetof(struct kernel_vm86_regs, pt.xgs));
ret += copy_to_user(&user->__null_gs, &regs->pt.xgs,
/* kernel_vm86_regs is missing xgs, so copy everything up to
(but not including) orig_eax, and then rest including orig_eax. */
ret += copy_to_user(user, regs, offsetof(struct kernel_vm86_regs, pt.orig_eax));
ret += copy_to_user(&user->orig_eax, &regs->pt.orig_eax,
sizeof(struct kernel_vm86_regs) -
offsetof(struct kernel_vm86_regs, pt.xgs));
offsetof(struct kernel_vm86_regs, pt.orig_eax));
return ret;
}
@ -113,12 +113,13 @@ static int copy_vm86_regs_from_user(struct kernel_vm86_regs *regs,
{
int ret = 0;
ret += copy_from_user(regs, user, offsetof(struct kernel_vm86_regs, pt.xgs));
ret += copy_from_user(&regs->pt.xgs, &user->__null_gs,
/* copy eax-xfs inclusive */
ret += copy_from_user(regs, user, offsetof(struct kernel_vm86_regs, pt.orig_eax));
/* copy orig_eax-__gsh+extra */
ret += copy_from_user(&regs->pt.orig_eax, &user->orig_eax,
sizeof(struct kernel_vm86_regs) -
offsetof(struct kernel_vm86_regs, pt.xgs) +
offsetof(struct kernel_vm86_regs, pt.orig_eax) +
extra);
return ret;
}
@ -157,8 +158,8 @@ struct pt_regs * fastcall save_v86_state(struct kernel_vm86_regs * regs)
ret = KVM86->regs32;
loadsegment(fs, current->thread.saved_fs);
ret->xgs = current->thread.saved_gs;
ret->xfs = current->thread.saved_fs;
loadsegment(gs, current->thread.saved_gs);
return ret;
}
@ -285,9 +286,9 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
*/
info->regs.pt.xds = 0;
info->regs.pt.xes = 0;
info->regs.pt.xgs = 0;
info->regs.pt.xfs = 0;
/* we are clearing fs later just before "jmp resume_userspace",
/* we are clearing gs later just before "jmp resume_userspace",
* because it is not saved/restored.
*/
@ -321,8 +322,8 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
*/
info->regs32->eax = 0;
tsk->thread.saved_esp0 = tsk->thread.esp0;
savesegment(fs, tsk->thread.saved_fs);
tsk->thread.saved_gs = info->regs32->xgs;
tsk->thread.saved_fs = info->regs32->xfs;
savesegment(gs, tsk->thread.saved_gs);
tss = &per_cpu(init_tss, get_cpu());
tsk->thread.esp0 = (unsigned long) &info->VM86_TSS_ESP0;
@ -342,7 +343,7 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
__asm__ __volatile__(
"movl %0,%%esp\n\t"
"movl %1,%%ebp\n\t"
"mov %2, %%fs\n\t"
"mov %2, %%gs\n\t"
"jmp resume_userspace"
: /* no outputs */
:"r" (&info->regs), "r" (task_thread_info(tsk)), "r" (0));

949
arch/i386/kernel/vmi.c Normal file
View File

@ -0,0 +1,949 @@
/*
* VMI specific paravirt-ops implementation
*
* Copyright (C) 2005, VMware, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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, GOOD TITLE or
* NON INFRINGEMENT. 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to zach@vmware.com
*
*/
#include <linux/module.h>
#include <linux/license.h>
#include <linux/cpu.h>
#include <linux/bootmem.h>
#include <linux/mm.h>
#include <asm/vmi.h>
#include <asm/io.h>
#include <asm/fixmap.h>
#include <asm/apicdef.h>
#include <asm/apic.h>
#include <asm/processor.h>
#include <asm/timer.h>
#include <asm/vmi_time.h>
/* Convenient for calling VMI functions indirectly in the ROM */
typedef u32 __attribute__((regparm(1))) (VROMFUNC)(void);
typedef u64 __attribute__((regparm(2))) (VROMLONGFUNC)(int);
#define call_vrom_func(rom,func) \
(((VROMFUNC *)(rom->func))())
#define call_vrom_long_func(rom,func,arg) \
(((VROMLONGFUNC *)(rom->func)) (arg))
static struct vrom_header *vmi_rom;
static int license_gplok;
static int disable_nodelay;
static int disable_pge;
static int disable_pse;
static int disable_sep;
static int disable_tsc;
static int disable_mtrr;
/* Cached VMI operations */
struct {
void (*cpuid)(void /* non-c */);
void (*_set_ldt)(u32 selector);
void (*set_tr)(u32 selector);
void (*set_kernel_stack)(u32 selector, u32 esp0);
void (*allocate_page)(u32, u32, u32, u32, u32);
void (*release_page)(u32, u32);
void (*set_pte)(pte_t, pte_t *, unsigned);
void (*update_pte)(pte_t *, unsigned);
void (*set_linear_mapping)(int, u32, u32, u32);
void (*flush_tlb)(int);
void (*set_initial_ap_state)(int, int);
void (*halt)(void);
} vmi_ops;
/* XXX move this to alternative.h */
extern struct paravirt_patch __start_parainstructions[],
__stop_parainstructions[];
/*
* VMI patching routines.
*/
#define MNEM_CALL 0xe8
#define MNEM_JMP 0xe9
#define MNEM_RET 0xc3
static char irq_save_disable_callout[] = {
MNEM_CALL, 0, 0, 0, 0,
MNEM_CALL, 0, 0, 0, 0,
MNEM_RET
};
#define IRQ_PATCH_INT_MASK 0
#define IRQ_PATCH_DISABLE 5
static inline void patch_offset(unsigned char *eip, unsigned char *dest)
{
*(unsigned long *)(eip+1) = dest-eip-5;
}
static unsigned patch_internal(int call, unsigned len, void *insns)
{
u64 reloc;
struct vmi_relocation_info *const rel = (struct vmi_relocation_info *)&reloc;
reloc = call_vrom_long_func(vmi_rom, get_reloc, call);
switch(rel->type) {
case VMI_RELOCATION_CALL_REL:
BUG_ON(len < 5);
*(char *)insns = MNEM_CALL;
patch_offset(insns, rel->eip);
return 5;
case VMI_RELOCATION_JUMP_REL:
BUG_ON(len < 5);
*(char *)insns = MNEM_JMP;
patch_offset(insns, rel->eip);
return 5;
case VMI_RELOCATION_NOP:
/* obliterate the whole thing */
return 0;
case VMI_RELOCATION_NONE:
/* leave native code in place */
break;
default:
BUG();
}
return len;
}
/*
* Apply patch if appropriate, return length of new instruction
* sequence. The callee does nop padding for us.
*/
static unsigned vmi_patch(u8 type, u16 clobbers, void *insns, unsigned len)
{
switch (type) {
case PARAVIRT_IRQ_DISABLE:
return patch_internal(VMI_CALL_DisableInterrupts, len, insns);
case PARAVIRT_IRQ_ENABLE:
return patch_internal(VMI_CALL_EnableInterrupts, len, insns);
case PARAVIRT_RESTORE_FLAGS:
return patch_internal(VMI_CALL_SetInterruptMask, len, insns);
case PARAVIRT_SAVE_FLAGS:
return patch_internal(VMI_CALL_GetInterruptMask, len, insns);
case PARAVIRT_SAVE_FLAGS_IRQ_DISABLE:
if (len >= 10) {
patch_internal(VMI_CALL_GetInterruptMask, len, insns);
patch_internal(VMI_CALL_DisableInterrupts, len-5, insns+5);
return 10;
} else {
/*
* You bastards didn't leave enough room to
* patch save_flags_irq_disable inline. Patch
* to a helper
*/
BUG_ON(len < 5);
*(char *)insns = MNEM_CALL;
patch_offset(insns, irq_save_disable_callout);
return 5;
}
case PARAVIRT_INTERRUPT_RETURN:
return patch_internal(VMI_CALL_IRET, len, insns);
case PARAVIRT_STI_SYSEXIT:
return patch_internal(VMI_CALL_SYSEXIT, len, insns);
default:
break;
}
return len;
}
/* CPUID has non-C semantics, and paravirt-ops API doesn't match hardware ISA */
static void vmi_cpuid(unsigned int *eax, unsigned int *ebx,
unsigned int *ecx, unsigned int *edx)
{
int override = 0;
if (*eax == 1)
override = 1;
asm volatile ("call *%6"
: "=a" (*eax),
"=b" (*ebx),
"=c" (*ecx),
"=d" (*edx)
: "0" (*eax), "2" (*ecx), "r" (vmi_ops.cpuid));
if (override) {
if (disable_pse)
*edx &= ~X86_FEATURE_PSE;
if (disable_pge)
*edx &= ~X86_FEATURE_PGE;
if (disable_sep)
*edx &= ~X86_FEATURE_SEP;
if (disable_tsc)
*edx &= ~X86_FEATURE_TSC;
if (disable_mtrr)
*edx &= ~X86_FEATURE_MTRR;
}
}
static inline void vmi_maybe_load_tls(struct desc_struct *gdt, int nr, struct desc_struct *new)
{
if (gdt[nr].a != new->a || gdt[nr].b != new->b)
write_gdt_entry(gdt, nr, new->a, new->b);
}
static void vmi_load_tls(struct thread_struct *t, unsigned int cpu)
{
struct desc_struct *gdt = get_cpu_gdt_table(cpu);
vmi_maybe_load_tls(gdt, GDT_ENTRY_TLS_MIN + 0, &t->tls_array[0]);
vmi_maybe_load_tls(gdt, GDT_ENTRY_TLS_MIN + 1, &t->tls_array[1]);
vmi_maybe_load_tls(gdt, GDT_ENTRY_TLS_MIN + 2, &t->tls_array[2]);
}
static void vmi_set_ldt(const void *addr, unsigned entries)
{
unsigned cpu = smp_processor_id();
u32 low, high;
pack_descriptor(&low, &high, (unsigned long)addr,
entries * sizeof(struct desc_struct) - 1,
DESCTYPE_LDT, 0);
write_gdt_entry(get_cpu_gdt_table(cpu), GDT_ENTRY_LDT, low, high);
vmi_ops._set_ldt(entries ? GDT_ENTRY_LDT*sizeof(struct desc_struct) : 0);
}
static void vmi_set_tr(void)
{
vmi_ops.set_tr(GDT_ENTRY_TSS*sizeof(struct desc_struct));
}
static void vmi_load_esp0(struct tss_struct *tss,
struct thread_struct *thread)
{
tss->esp0 = thread->esp0;
/* This can only happen when SEP is enabled, no need to test "SEP"arately */
if (unlikely(tss->ss1 != thread->sysenter_cs)) {
tss->ss1 = thread->sysenter_cs;
wrmsr(MSR_IA32_SYSENTER_CS, thread->sysenter_cs, 0);
}
vmi_ops.set_kernel_stack(__KERNEL_DS, tss->esp0);
}
static void vmi_flush_tlb_user(void)
{
vmi_ops.flush_tlb(VMI_FLUSH_TLB);
}
static void vmi_flush_tlb_kernel(void)
{
vmi_ops.flush_tlb(VMI_FLUSH_TLB | VMI_FLUSH_GLOBAL);
}
/* Stub to do nothing at all; used for delays and unimplemented calls */
static void vmi_nop(void)
{
}
/* For NO_IDLE_HZ, we stop the clock when halting the kernel */
#ifdef CONFIG_NO_IDLE_HZ
static fastcall void vmi_safe_halt(void)
{
int idle = vmi_stop_hz_timer();
vmi_ops.halt();
if (idle) {
local_irq_disable();
vmi_account_time_restart_hz_timer();
local_irq_enable();
}
}
#endif
#ifdef CONFIG_DEBUG_PAGE_TYPE
#ifdef CONFIG_X86_PAE
#define MAX_BOOT_PTS (2048+4+1)
#else
#define MAX_BOOT_PTS (1024+1)
#endif
/*
* During boot, mem_map is not yet available in paging_init, so stash
* all the boot page allocations here.
*/
static struct {
u32 pfn;
int type;
} boot_page_allocations[MAX_BOOT_PTS];
static int num_boot_page_allocations;
static int boot_allocations_applied;
void vmi_apply_boot_page_allocations(void)
{
int i;
BUG_ON(!mem_map);
for (i = 0; i < num_boot_page_allocations; i++) {
struct page *page = pfn_to_page(boot_page_allocations[i].pfn);
page->type = boot_page_allocations[i].type;
page->type = boot_page_allocations[i].type &
~(VMI_PAGE_ZEROED | VMI_PAGE_CLONE);
}
boot_allocations_applied = 1;
}
static void record_page_type(u32 pfn, int type)
{
BUG_ON(num_boot_page_allocations >= MAX_BOOT_PTS);
boot_page_allocations[num_boot_page_allocations].pfn = pfn;
boot_page_allocations[num_boot_page_allocations].type = type;
num_boot_page_allocations++;
}
static void check_zeroed_page(u32 pfn, int type, struct page *page)
{
u32 *ptr;
int i;
int limit = PAGE_SIZE / sizeof(int);
if (page_address(page))
ptr = (u32 *)page_address(page);
else
ptr = (u32 *)__va(pfn << PAGE_SHIFT);
/*
* When cloning the root in non-PAE mode, only the userspace
* pdes need to be zeroed.
*/
if (type & VMI_PAGE_CLONE)
limit = USER_PTRS_PER_PGD;
for (i = 0; i < limit; i++)
BUG_ON(ptr[i]);
}
/*
* We stash the page type into struct page so we can verify the page
* types are used properly.
*/
static void vmi_set_page_type(u32 pfn, int type)
{
/* PAE can have multiple roots per page - don't track */
if (PTRS_PER_PMD > 1 && (type & VMI_PAGE_PDP))
return;
if (boot_allocations_applied) {
struct page *page = pfn_to_page(pfn);
if (type != VMI_PAGE_NORMAL)
BUG_ON(page->type);
else
BUG_ON(page->type == VMI_PAGE_NORMAL);
page->type = type & ~(VMI_PAGE_ZEROED | VMI_PAGE_CLONE);
if (type & VMI_PAGE_ZEROED)
check_zeroed_page(pfn, type, page);
} else {
record_page_type(pfn, type);
}
}
static void vmi_check_page_type(u32 pfn, int type)
{
/* PAE can have multiple roots per page - skip checks */
if (PTRS_PER_PMD > 1 && (type & VMI_PAGE_PDP))
return;
type &= ~(VMI_PAGE_ZEROED | VMI_PAGE_CLONE);
if (boot_allocations_applied) {
struct page *page = pfn_to_page(pfn);
BUG_ON((page->type ^ type) & VMI_PAGE_PAE);
BUG_ON(type == VMI_PAGE_NORMAL && page->type);
BUG_ON((type & page->type) == 0);
}
}
#else
#define vmi_set_page_type(p,t) do { } while (0)
#define vmi_check_page_type(p,t) do { } while (0)
#endif
static void vmi_allocate_pt(u32 pfn)
{
vmi_set_page_type(pfn, VMI_PAGE_L1);
vmi_ops.allocate_page(pfn, VMI_PAGE_L1, 0, 0, 0);
}
static void vmi_allocate_pd(u32 pfn)
{
/*
* This call comes in very early, before mem_map is setup.
* It is called only for swapper_pg_dir, which already has
* data on it.
*/
vmi_set_page_type(pfn, VMI_PAGE_L2);
vmi_ops.allocate_page(pfn, VMI_PAGE_L2, 0, 0, 0);
}
static void vmi_allocate_pd_clone(u32 pfn, u32 clonepfn, u32 start, u32 count)
{
vmi_set_page_type(pfn, VMI_PAGE_L2 | VMI_PAGE_CLONE);
vmi_check_page_type(clonepfn, VMI_PAGE_L2);
vmi_ops.allocate_page(pfn, VMI_PAGE_L2 | VMI_PAGE_CLONE, clonepfn, start, count);
}
static void vmi_release_pt(u32 pfn)
{
vmi_ops.release_page(pfn, VMI_PAGE_L1);
vmi_set_page_type(pfn, VMI_PAGE_NORMAL);
}
static void vmi_release_pd(u32 pfn)
{
vmi_ops.release_page(pfn, VMI_PAGE_L2);
vmi_set_page_type(pfn, VMI_PAGE_NORMAL);
}
/*
* Helper macros for MMU update flags. We can defer updates until a flush
* or page invalidation only if the update is to the current address space
* (otherwise, there is no flush). We must check against init_mm, since
* this could be a kernel update, which usually passes init_mm, although
* sometimes this check can be skipped if we know the particular function
* is only called on user mode PTEs. We could change the kernel to pass
* current->active_mm here, but in particular, I was unsure if changing
* mm/highmem.c to do this would still be correct on other architectures.
*/
#define is_current_as(mm, mustbeuser) ((mm) == current->active_mm || \
(!mustbeuser && (mm) == &init_mm))
#define vmi_flags_addr(mm, addr, level, user) \
((level) | (is_current_as(mm, user) ? \
(VMI_PAGE_CURRENT_AS | ((addr) & VMI_PAGE_VA_MASK)) : 0))
#define vmi_flags_addr_defer(mm, addr, level, user) \
((level) | (is_current_as(mm, user) ? \
(VMI_PAGE_DEFER | VMI_PAGE_CURRENT_AS | ((addr) & VMI_PAGE_VA_MASK)) : 0))
static void vmi_update_pte(struct mm_struct *mm, u32 addr, pte_t *ptep)
{
vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE);
vmi_ops.update_pte(ptep, vmi_flags_addr(mm, addr, VMI_PAGE_PT, 0));
}
static void vmi_update_pte_defer(struct mm_struct *mm, u32 addr, pte_t *ptep)
{
vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE);
vmi_ops.update_pte(ptep, vmi_flags_addr_defer(mm, addr, VMI_PAGE_PT, 0));
}
static void vmi_set_pte(pte_t *ptep, pte_t pte)
{
/* XXX because of set_pmd_pte, this can be called on PT or PD layers */
vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE | VMI_PAGE_PD);
vmi_ops.set_pte(pte, ptep, VMI_PAGE_PT);
}
static void vmi_set_pte_at(struct mm_struct *mm, u32 addr, pte_t *ptep, pte_t pte)
{
vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE);
vmi_ops.set_pte(pte, ptep, vmi_flags_addr(mm, addr, VMI_PAGE_PT, 0));
}
static void vmi_set_pmd(pmd_t *pmdp, pmd_t pmdval)
{
#ifdef CONFIG_X86_PAE
const pte_t pte = { pmdval.pmd, pmdval.pmd >> 32 };
vmi_check_page_type(__pa(pmdp) >> PAGE_SHIFT, VMI_PAGE_PMD);
#else
const pte_t pte = { pmdval.pud.pgd.pgd };
vmi_check_page_type(__pa(pmdp) >> PAGE_SHIFT, VMI_PAGE_PGD);
#endif
vmi_ops.set_pte(pte, (pte_t *)pmdp, VMI_PAGE_PD);
}
#ifdef CONFIG_X86_PAE
static void vmi_set_pte_atomic(pte_t *ptep, pte_t pteval)
{
/*
* XXX This is called from set_pmd_pte, but at both PT
* and PD layers so the VMI_PAGE_PT flag is wrong. But
* it is only called for large page mapping changes,
* the Xen backend, doesn't support large pages, and the
* ESX backend doesn't depend on the flag.
*/
set_64bit((unsigned long long *)ptep,pte_val(pteval));
vmi_ops.update_pte(ptep, VMI_PAGE_PT);
}
static void vmi_set_pte_present(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte)
{
vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE);
vmi_ops.set_pte(pte, ptep, vmi_flags_addr_defer(mm, addr, VMI_PAGE_PT, 1));
}
static void vmi_set_pud(pud_t *pudp, pud_t pudval)
{
/* Um, eww */
const pte_t pte = { pudval.pgd.pgd, pudval.pgd.pgd >> 32 };
vmi_check_page_type(__pa(pudp) >> PAGE_SHIFT, VMI_PAGE_PGD);
vmi_ops.set_pte(pte, (pte_t *)pudp, VMI_PAGE_PDP);
}
static void vmi_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{
const pte_t pte = { 0 };
vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE);
vmi_ops.set_pte(pte, ptep, vmi_flags_addr(mm, addr, VMI_PAGE_PT, 0));
}
void vmi_pmd_clear(pmd_t *pmd)
{
const pte_t pte = { 0 };
vmi_check_page_type(__pa(pmd) >> PAGE_SHIFT, VMI_PAGE_PMD);
vmi_ops.set_pte(pte, (pte_t *)pmd, VMI_PAGE_PD);
}
#endif
#ifdef CONFIG_SMP
struct vmi_ap_state ap;
extern void setup_pda(void);
static void __init /* XXX cpu hotplug */
vmi_startup_ipi_hook(int phys_apicid, unsigned long start_eip,
unsigned long start_esp)
{
/* Default everything to zero. This is fine for most GPRs. */
memset(&ap, 0, sizeof(struct vmi_ap_state));
ap.gdtr_limit = GDT_SIZE - 1;
ap.gdtr_base = (unsigned long) get_cpu_gdt_table(phys_apicid);
ap.idtr_limit = IDT_ENTRIES * 8 - 1;
ap.idtr_base = (unsigned long) idt_table;
ap.ldtr = 0;
ap.cs = __KERNEL_CS;
ap.eip = (unsigned long) start_eip;
ap.ss = __KERNEL_DS;
ap.esp = (unsigned long) start_esp;
ap.ds = __USER_DS;
ap.es = __USER_DS;
ap.fs = __KERNEL_PDA;
ap.gs = 0;
ap.eflags = 0;
setup_pda();
#ifdef CONFIG_X86_PAE
/* efer should match BSP efer. */
if (cpu_has_nx) {
unsigned l, h;
rdmsr(MSR_EFER, l, h);
ap.efer = (unsigned long long) h << 32 | l;
}
#endif
ap.cr3 = __pa(swapper_pg_dir);
/* Protected mode, paging, AM, WP, NE, MP. */
ap.cr0 = 0x80050023;
ap.cr4 = mmu_cr4_features;
vmi_ops.set_initial_ap_state(__pa(&ap), phys_apicid);
}
#endif
static inline int __init check_vmi_rom(struct vrom_header *rom)
{
struct pci_header *pci;
struct pnp_header *pnp;
const char *manufacturer = "UNKNOWN";
const char *product = "UNKNOWN";
const char *license = "unspecified";
if (rom->rom_signature != 0xaa55)
return 0;
if (rom->vrom_signature != VMI_SIGNATURE)
return 0;
if (rom->api_version_maj != VMI_API_REV_MAJOR ||
rom->api_version_min+1 < VMI_API_REV_MINOR+1) {
printk(KERN_WARNING "VMI: Found mismatched rom version %d.%d\n",
rom->api_version_maj,
rom->api_version_min);
return 0;
}
/*
* Relying on the VMI_SIGNATURE field is not 100% safe, so check
* the PCI header and device type to make sure this is really a
* VMI device.
*/
if (!rom->pci_header_offs) {
printk(KERN_WARNING "VMI: ROM does not contain PCI header.\n");
return 0;
}
pci = (struct pci_header *)((char *)rom+rom->pci_header_offs);
if (pci->vendorID != PCI_VENDOR_ID_VMWARE ||
pci->deviceID != PCI_DEVICE_ID_VMWARE_VMI) {
/* Allow it to run... anyways, but warn */
printk(KERN_WARNING "VMI: ROM from unknown manufacturer\n");
}
if (rom->pnp_header_offs) {
pnp = (struct pnp_header *)((char *)rom+rom->pnp_header_offs);
if (pnp->manufacturer_offset)
manufacturer = (const char *)rom+pnp->manufacturer_offset;
if (pnp->product_offset)
product = (const char *)rom+pnp->product_offset;
}
if (rom->license_offs)
license = (char *)rom+rom->license_offs;
printk(KERN_INFO "VMI: Found %s %s, API version %d.%d, ROM version %d.%d\n",
manufacturer, product,
rom->api_version_maj, rom->api_version_min,
pci->rom_version_maj, pci->rom_version_min);
license_gplok = license_is_gpl_compatible(license);
if (!license_gplok) {
printk(KERN_WARNING "VMI: ROM license '%s' taints kernel... "
"inlining disabled\n",
license);
add_taint(TAINT_PROPRIETARY_MODULE);
}
return 1;
}
/*
* Probe for the VMI option ROM
*/
static inline int __init probe_vmi_rom(void)
{
unsigned long base;
/* VMI ROM is in option ROM area, check signature */
for (base = 0xC0000; base < 0xE0000; base += 2048) {
struct vrom_header *romstart;
romstart = (struct vrom_header *)isa_bus_to_virt(base);
if (check_vmi_rom(romstart)) {
vmi_rom = romstart;
return 1;
}
}
return 0;
}
/*
* VMI setup common to all processors
*/
void vmi_bringup(void)
{
/* We must establish the lowmem mapping for MMU ops to work */
if (vmi_rom)
vmi_ops.set_linear_mapping(0, __PAGE_OFFSET, max_low_pfn, 0);
}
/*
* Return a pointer to the VMI function or a NOP stub
*/
static void *vmi_get_function(int vmicall)
{
u64 reloc;
const struct vmi_relocation_info *rel = (struct vmi_relocation_info *)&reloc;
reloc = call_vrom_long_func(vmi_rom, get_reloc, vmicall);
BUG_ON(rel->type == VMI_RELOCATION_JUMP_REL);
if (rel->type == VMI_RELOCATION_CALL_REL)
return (void *)rel->eip;
else
return (void *)vmi_nop;
}
/*
* Helper macro for making the VMI paravirt-ops fill code readable.
* For unimplemented operations, fall back to default.
*/
#define para_fill(opname, vmicall) \
do { \
reloc = call_vrom_long_func(vmi_rom, get_reloc, \
VMI_CALL_##vmicall); \
if (rel->type != VMI_RELOCATION_NONE) { \
BUG_ON(rel->type != VMI_RELOCATION_CALL_REL); \
paravirt_ops.opname = (void *)rel->eip; \
} \
} while (0)
/*
* Activate the VMI interface and switch into paravirtualized mode
*/
static inline int __init activate_vmi(void)
{
short kernel_cs;
u64 reloc;
const struct vmi_relocation_info *rel = (struct vmi_relocation_info *)&reloc;
if (call_vrom_func(vmi_rom, vmi_init) != 0) {
printk(KERN_ERR "VMI ROM failed to initialize!");
return 0;
}
savesegment(cs, kernel_cs);
paravirt_ops.paravirt_enabled = 1;
paravirt_ops.kernel_rpl = kernel_cs & SEGMENT_RPL_MASK;
paravirt_ops.patch = vmi_patch;
paravirt_ops.name = "vmi";
/*
* Many of these operations are ABI compatible with VMI.
* This means we can fill in the paravirt-ops with direct
* pointers into the VMI ROM. If the calling convention for
* these operations changes, this code needs to be updated.
*
* Exceptions
* CPUID paravirt-op uses pointers, not the native ISA
* halt has no VMI equivalent; all VMI halts are "safe"
* no MSR support yet - just trap and emulate. VMI uses the
* same ABI as the native ISA, but Linux wants exceptions
* from bogus MSR read / write handled
* rdpmc is not yet used in Linux
*/
/* CPUID is special, so very special */
reloc = call_vrom_long_func(vmi_rom, get_reloc, VMI_CALL_CPUID);
if (rel->type != VMI_RELOCATION_NONE) {
BUG_ON(rel->type != VMI_RELOCATION_CALL_REL);
vmi_ops.cpuid = (void *)rel->eip;
paravirt_ops.cpuid = vmi_cpuid;
}
para_fill(clts, CLTS);
para_fill(get_debugreg, GetDR);
para_fill(set_debugreg, SetDR);
para_fill(read_cr0, GetCR0);
para_fill(read_cr2, GetCR2);
para_fill(read_cr3, GetCR3);
para_fill(read_cr4, GetCR4);
para_fill(write_cr0, SetCR0);
para_fill(write_cr2, SetCR2);
para_fill(write_cr3, SetCR3);
para_fill(write_cr4, SetCR4);
para_fill(save_fl, GetInterruptMask);
para_fill(restore_fl, SetInterruptMask);
para_fill(irq_disable, DisableInterrupts);
para_fill(irq_enable, EnableInterrupts);
/* irq_save_disable !!! sheer pain */
patch_offset(&irq_save_disable_callout[IRQ_PATCH_INT_MASK],
(char *)paravirt_ops.save_fl);
patch_offset(&irq_save_disable_callout[IRQ_PATCH_DISABLE],
(char *)paravirt_ops.irq_disable);
#ifndef CONFIG_NO_IDLE_HZ
para_fill(safe_halt, Halt);
#else
vmi_ops.halt = vmi_get_function(VMI_CALL_Halt);
paravirt_ops.safe_halt = vmi_safe_halt;
#endif
para_fill(wbinvd, WBINVD);
/* paravirt_ops.read_msr = vmi_rdmsr */
/* paravirt_ops.write_msr = vmi_wrmsr */
para_fill(read_tsc, RDTSC);
/* paravirt_ops.rdpmc = vmi_rdpmc */
/* TR interface doesn't pass TR value */
reloc = call_vrom_long_func(vmi_rom, get_reloc, VMI_CALL_SetTR);
if (rel->type != VMI_RELOCATION_NONE) {
BUG_ON(rel->type != VMI_RELOCATION_CALL_REL);
vmi_ops.set_tr = (void *)rel->eip;
paravirt_ops.load_tr_desc = vmi_set_tr;
}
/* LDT is special, too */
reloc = call_vrom_long_func(vmi_rom, get_reloc, VMI_CALL_SetLDT);
if (rel->type != VMI_RELOCATION_NONE) {
BUG_ON(rel->type != VMI_RELOCATION_CALL_REL);
vmi_ops._set_ldt = (void *)rel->eip;
paravirt_ops.set_ldt = vmi_set_ldt;
}
para_fill(load_gdt, SetGDT);
para_fill(load_idt, SetIDT);
para_fill(store_gdt, GetGDT);
para_fill(store_idt, GetIDT);
para_fill(store_tr, GetTR);
paravirt_ops.load_tls = vmi_load_tls;
para_fill(write_ldt_entry, WriteLDTEntry);
para_fill(write_gdt_entry, WriteGDTEntry);
para_fill(write_idt_entry, WriteIDTEntry);
reloc = call_vrom_long_func(vmi_rom, get_reloc,
VMI_CALL_UpdateKernelStack);
if (rel->type != VMI_RELOCATION_NONE) {
BUG_ON(rel->type != VMI_RELOCATION_CALL_REL);
vmi_ops.set_kernel_stack = (void *)rel->eip;
paravirt_ops.load_esp0 = vmi_load_esp0;
}
para_fill(set_iopl_mask, SetIOPLMask);
paravirt_ops.io_delay = (void *)vmi_nop;
if (!disable_nodelay) {
paravirt_ops.const_udelay = (void *)vmi_nop;
}
para_fill(set_lazy_mode, SetLazyMode);
reloc = call_vrom_long_func(vmi_rom, get_reloc, VMI_CALL_FlushTLB);
if (rel->type != VMI_RELOCATION_NONE) {
vmi_ops.flush_tlb = (void *)rel->eip;
paravirt_ops.flush_tlb_user = vmi_flush_tlb_user;
paravirt_ops.flush_tlb_kernel = vmi_flush_tlb_kernel;
}
para_fill(flush_tlb_single, InvalPage);
/*
* Until a standard flag format can be agreed on, we need to
* implement these as wrappers in Linux. Get the VMI ROM
* function pointers for the two backend calls.
*/
#ifdef CONFIG_X86_PAE
vmi_ops.set_pte = vmi_get_function(VMI_CALL_SetPxELong);
vmi_ops.update_pte = vmi_get_function(VMI_CALL_UpdatePxELong);
#else
vmi_ops.set_pte = vmi_get_function(VMI_CALL_SetPxE);
vmi_ops.update_pte = vmi_get_function(VMI_CALL_UpdatePxE);
#endif
vmi_ops.set_linear_mapping = vmi_get_function(VMI_CALL_SetLinearMapping);
vmi_ops.allocate_page = vmi_get_function(VMI_CALL_AllocatePage);
vmi_ops.release_page = vmi_get_function(VMI_CALL_ReleasePage);
paravirt_ops.alloc_pt = vmi_allocate_pt;
paravirt_ops.alloc_pd = vmi_allocate_pd;
paravirt_ops.alloc_pd_clone = vmi_allocate_pd_clone;
paravirt_ops.release_pt = vmi_release_pt;
paravirt_ops.release_pd = vmi_release_pd;
paravirt_ops.set_pte = vmi_set_pte;
paravirt_ops.set_pte_at = vmi_set_pte_at;
paravirt_ops.set_pmd = vmi_set_pmd;
paravirt_ops.pte_update = vmi_update_pte;
paravirt_ops.pte_update_defer = vmi_update_pte_defer;
#ifdef CONFIG_X86_PAE
paravirt_ops.set_pte_atomic = vmi_set_pte_atomic;
paravirt_ops.set_pte_present = vmi_set_pte_present;
paravirt_ops.set_pud = vmi_set_pud;
paravirt_ops.pte_clear = vmi_pte_clear;
paravirt_ops.pmd_clear = vmi_pmd_clear;
#endif
/*
* These MUST always be patched. Don't support indirect jumps
* through these operations, as the VMI interface may use either
* a jump or a call to get to these operations, depending on
* the backend. They are performance critical anyway, so requiring
* a patch is not a big problem.
*/
paravirt_ops.irq_enable_sysexit = (void *)0xfeedbab0;
paravirt_ops.iret = (void *)0xbadbab0;
#ifdef CONFIG_SMP
paravirt_ops.startup_ipi_hook = vmi_startup_ipi_hook;
vmi_ops.set_initial_ap_state = vmi_get_function(VMI_CALL_SetInitialAPState);
#endif
#ifdef CONFIG_X86_LOCAL_APIC
paravirt_ops.apic_read = vmi_get_function(VMI_CALL_APICRead);
paravirt_ops.apic_write = vmi_get_function(VMI_CALL_APICWrite);
paravirt_ops.apic_write_atomic = vmi_get_function(VMI_CALL_APICWrite);
#endif
/*
* Check for VMI timer functionality by probing for a cycle frequency method
*/
reloc = call_vrom_long_func(vmi_rom, get_reloc, VMI_CALL_GetCycleFrequency);
if (rel->type != VMI_RELOCATION_NONE) {
vmi_timer_ops.get_cycle_frequency = (void *)rel->eip;
vmi_timer_ops.get_cycle_counter =
vmi_get_function(VMI_CALL_GetCycleCounter);
vmi_timer_ops.get_wallclock =
vmi_get_function(VMI_CALL_GetWallclockTime);
vmi_timer_ops.wallclock_updated =
vmi_get_function(VMI_CALL_WallclockUpdated);
vmi_timer_ops.set_alarm = vmi_get_function(VMI_CALL_SetAlarm);
vmi_timer_ops.cancel_alarm =
vmi_get_function(VMI_CALL_CancelAlarm);
paravirt_ops.time_init = vmi_time_init;
paravirt_ops.get_wallclock = vmi_get_wallclock;
paravirt_ops.set_wallclock = vmi_set_wallclock;
#ifdef CONFIG_X86_LOCAL_APIC
paravirt_ops.setup_boot_clock = vmi_timer_setup_boot_alarm;
paravirt_ops.setup_secondary_clock = vmi_timer_setup_secondary_alarm;
#endif
custom_sched_clock = vmi_sched_clock;
}
/*
* Alternative instruction rewriting doesn't happen soon enough
* to convert VMI_IRET to a call instead of a jump; so we have
* to do this before IRQs get reenabled. Fortunately, it is
* idempotent.
*/
apply_paravirt(__start_parainstructions, __stop_parainstructions);
vmi_bringup();
return 1;
}
#undef para_fill
void __init vmi_init(void)
{
unsigned long flags;
if (!vmi_rom)
probe_vmi_rom();
else
check_vmi_rom(vmi_rom);
/* In case probing for or validating the ROM failed, basil */
if (!vmi_rom)
return;
reserve_top_address(-vmi_rom->virtual_top);
local_irq_save(flags);
activate_vmi();
#ifdef CONFIG_SMP
no_timer_check = 1;
#endif
local_irq_restore(flags & X86_EFLAGS_IF);
}
static int __init parse_vmi(char *arg)
{
if (!arg)
return -EINVAL;
if (!strcmp(arg, "disable_nodelay"))
disable_nodelay = 1;
else if (!strcmp(arg, "disable_pge")) {
clear_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability);
disable_pge = 1;
} else if (!strcmp(arg, "disable_pse")) {
clear_bit(X86_FEATURE_PSE, boot_cpu_data.x86_capability);
disable_pse = 1;
} else if (!strcmp(arg, "disable_sep")) {
clear_bit(X86_FEATURE_SEP, boot_cpu_data.x86_capability);
disable_sep = 1;
} else if (!strcmp(arg, "disable_tsc")) {
clear_bit(X86_FEATURE_TSC, boot_cpu_data.x86_capability);
disable_tsc = 1;
} else if (!strcmp(arg, "disable_mtrr")) {
clear_bit(X86_FEATURE_MTRR, boot_cpu_data.x86_capability);
disable_mtrr = 1;
}
return 0;
}
early_param("vmi", parse_vmi);

499
arch/i386/kernel/vmitime.c Normal file
View File

@ -0,0 +1,499 @@
/*
* VMI paravirtual timer support routines.
*
* Copyright (C) 2005, VMware, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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, GOOD TITLE or
* NON INFRINGEMENT. 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to dhecht@vmware.com
*
*/
/*
* Portions of this code from arch/i386/kernel/timers/timer_tsc.c.
* Portions of the CONFIG_NO_IDLE_HZ code from arch/s390/kernel/time.c.
* See comments there for proper credits.
*/
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/rcupdate.h>
#include <linux/clocksource.h>
#include <asm/timer.h>
#include <asm/io.h>
#include <asm/apic.h>
#include <asm/div64.h>
#include <asm/timer.h>
#include <asm/desc.h>
#include <asm/vmi.h>
#include <asm/vmi_time.h>
#include <mach_timer.h>
#include <io_ports.h>
#ifdef CONFIG_X86_LOCAL_APIC
#define VMI_ALARM_WIRING VMI_ALARM_WIRED_LVTT
#else
#define VMI_ALARM_WIRING VMI_ALARM_WIRED_IRQ0
#endif
/* Cached VMI operations */
struct vmi_timer_ops vmi_timer_ops;
#ifdef CONFIG_NO_IDLE_HZ
/* /proc/sys/kernel/hz_timer state. */
int sysctl_hz_timer;
/* Some stats */
static DEFINE_PER_CPU(unsigned long, vmi_idle_no_hz_irqs);
static DEFINE_PER_CPU(unsigned long, vmi_idle_no_hz_jiffies);
static DEFINE_PER_CPU(unsigned long, idle_start_jiffies);
#endif /* CONFIG_NO_IDLE_HZ */
/* Number of alarms per second. By default this is CONFIG_VMI_ALARM_HZ. */
static int alarm_hz = CONFIG_VMI_ALARM_HZ;
/* Cache of the value get_cycle_frequency / HZ. */
static signed long long cycles_per_jiffy;
/* Cache of the value get_cycle_frequency / alarm_hz. */
static signed long long cycles_per_alarm;
/* The number of cycles accounted for by the 'jiffies'/'xtime' count.
* Protected by xtime_lock. */
static unsigned long long real_cycles_accounted_system;
/* The number of cycles accounted for by update_process_times(), per cpu. */
static DEFINE_PER_CPU(unsigned long long, process_times_cycles_accounted_cpu);
/* The number of stolen cycles accounted, per cpu. */
static DEFINE_PER_CPU(unsigned long long, stolen_cycles_accounted_cpu);
/* Clock source. */
static cycle_t read_real_cycles(void)
{
return vmi_timer_ops.get_cycle_counter(VMI_CYCLES_REAL);
}
static cycle_t read_available_cycles(void)
{
return vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE);
}
#if 0
static cycle_t read_stolen_cycles(void)
{
return vmi_timer_ops.get_cycle_counter(VMI_CYCLES_STOLEN);
}
#endif /* 0 */
static struct clocksource clocksource_vmi = {
.name = "vmi-timer",
.rating = 450,
.read = read_real_cycles,
.mask = CLOCKSOURCE_MASK(64),
.mult = 0, /* to be set */
.shift = 22,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
/* Timer interrupt handler. */
static irqreturn_t vmi_timer_interrupt(int irq, void *dev_id);
static struct irqaction vmi_timer_irq = {
vmi_timer_interrupt,
SA_INTERRUPT,
CPU_MASK_NONE,
"VMI-alarm",
NULL,
NULL
};
/* Alarm rate */
static int __init vmi_timer_alarm_rate_setup(char* str)
{
int alarm_rate;
if (get_option(&str, &alarm_rate) == 1 && alarm_rate > 0) {
alarm_hz = alarm_rate;
printk(KERN_WARNING "VMI timer alarm HZ set to %d\n", alarm_hz);
}
return 1;
}
__setup("vmi_timer_alarm_hz=", vmi_timer_alarm_rate_setup);
/* Initialization */
static void vmi_get_wallclock_ts(struct timespec *ts)
{
unsigned long long wallclock;
wallclock = vmi_timer_ops.get_wallclock(); // nsec units
ts->tv_nsec = do_div(wallclock, 1000000000);
ts->tv_sec = wallclock;
}
static void update_xtime_from_wallclock(void)
{
struct timespec ts;
vmi_get_wallclock_ts(&ts);
do_settimeofday(&ts);
}
unsigned long vmi_get_wallclock(void)
{
struct timespec ts;
vmi_get_wallclock_ts(&ts);
return ts.tv_sec;
}
int vmi_set_wallclock(unsigned long now)
{
return -1;
}
unsigned long long vmi_sched_clock(void)
{
return read_available_cycles();
}
void __init vmi_time_init(void)
{
unsigned long long cycles_per_sec, cycles_per_msec;
unsigned long flags;
local_irq_save(flags);
setup_irq(0, &vmi_timer_irq);
#ifdef CONFIG_X86_LOCAL_APIC
set_intr_gate(LOCAL_TIMER_VECTOR, apic_vmi_timer_interrupt);
#endif
no_sync_cmos_clock = 1;
vmi_get_wallclock_ts(&xtime);
set_normalized_timespec(&wall_to_monotonic,
-xtime.tv_sec, -xtime.tv_nsec);
real_cycles_accounted_system = read_real_cycles();
update_xtime_from_wallclock();
per_cpu(process_times_cycles_accounted_cpu, 0) = read_available_cycles();
cycles_per_sec = vmi_timer_ops.get_cycle_frequency();
cycles_per_jiffy = cycles_per_sec;
(void)do_div(cycles_per_jiffy, HZ);
cycles_per_alarm = cycles_per_sec;
(void)do_div(cycles_per_alarm, alarm_hz);
cycles_per_msec = cycles_per_sec;
(void)do_div(cycles_per_msec, 1000);
cpu_khz = cycles_per_msec;
printk(KERN_WARNING "VMI timer cycles/sec = %llu ; cycles/jiffy = %llu ;"
"cycles/alarm = %llu\n", cycles_per_sec, cycles_per_jiffy,
cycles_per_alarm);
clocksource_vmi.mult = clocksource_khz2mult(cycles_per_msec,
clocksource_vmi.shift);
if (clocksource_register(&clocksource_vmi))
printk(KERN_WARNING "Error registering VMITIME clocksource.");
/* Disable PIT. */
outb_p(0x3a, PIT_MODE); /* binary, mode 5, LSB/MSB, ch 0 */
/* schedule the alarm. do this in phase with process_times_cycles_accounted_cpu
* reduce the latency calling update_process_times. */
vmi_timer_ops.set_alarm(
VMI_ALARM_WIRED_IRQ0 | VMI_ALARM_IS_PERIODIC | VMI_CYCLES_AVAILABLE,
per_cpu(process_times_cycles_accounted_cpu, 0) + cycles_per_alarm,
cycles_per_alarm);
local_irq_restore(flags);
}
#ifdef CONFIG_X86_LOCAL_APIC
void __init vmi_timer_setup_boot_alarm(void)
{
local_irq_disable();
/* Route the interrupt to the correct vector. */
apic_write_around(APIC_LVTT, LOCAL_TIMER_VECTOR);
/* Cancel the IRQ0 wired alarm, and setup the LVTT alarm. */
vmi_timer_ops.cancel_alarm(VMI_CYCLES_AVAILABLE);
vmi_timer_ops.set_alarm(
VMI_ALARM_WIRED_LVTT | VMI_ALARM_IS_PERIODIC | VMI_CYCLES_AVAILABLE,
per_cpu(process_times_cycles_accounted_cpu, 0) + cycles_per_alarm,
cycles_per_alarm);
local_irq_enable();
}
/* Initialize the time accounting variables for an AP on an SMP system.
* Also, set the local alarm for the AP. */
void __init vmi_timer_setup_secondary_alarm(void)
{
int cpu = smp_processor_id();
/* Route the interrupt to the correct vector. */
apic_write_around(APIC_LVTT, LOCAL_TIMER_VECTOR);
per_cpu(process_times_cycles_accounted_cpu, cpu) = read_available_cycles();
vmi_timer_ops.set_alarm(
VMI_ALARM_WIRED_LVTT | VMI_ALARM_IS_PERIODIC | VMI_CYCLES_AVAILABLE,
per_cpu(process_times_cycles_accounted_cpu, cpu) + cycles_per_alarm,
cycles_per_alarm);
}
#endif
/* Update system wide (real) time accounting (e.g. jiffies, xtime). */
static void vmi_account_real_cycles(unsigned long long cur_real_cycles)
{
long long cycles_not_accounted;
write_seqlock(&xtime_lock);
cycles_not_accounted = cur_real_cycles - real_cycles_accounted_system;
while (cycles_not_accounted >= cycles_per_jiffy) {
/* systems wide jiffies and wallclock. */
do_timer(1);
cycles_not_accounted -= cycles_per_jiffy;
real_cycles_accounted_system += cycles_per_jiffy;
}
if (vmi_timer_ops.wallclock_updated())
update_xtime_from_wallclock();
write_sequnlock(&xtime_lock);
}
/* Update per-cpu process times. */
static void vmi_account_process_times_cycles(struct pt_regs *regs, int cpu,
unsigned long long cur_process_times_cycles)
{
long long cycles_not_accounted;
cycles_not_accounted = cur_process_times_cycles -
per_cpu(process_times_cycles_accounted_cpu, cpu);
while (cycles_not_accounted >= cycles_per_jiffy) {
/* Account time to the current process. This includes
* calling into the scheduler to decrement the timeslice
* and possibly reschedule.*/
update_process_times(user_mode(regs));
/* XXX handle /proc/profile multiplier. */
profile_tick(CPU_PROFILING);
cycles_not_accounted -= cycles_per_jiffy;
per_cpu(process_times_cycles_accounted_cpu, cpu) += cycles_per_jiffy;
}
}
#ifdef CONFIG_NO_IDLE_HZ
/* Update per-cpu idle times. Used when a no-hz halt is ended. */
static void vmi_account_no_hz_idle_cycles(int cpu,
unsigned long long cur_process_times_cycles)
{
long long cycles_not_accounted;
unsigned long no_idle_hz_jiffies = 0;
cycles_not_accounted = cur_process_times_cycles -
per_cpu(process_times_cycles_accounted_cpu, cpu);
while (cycles_not_accounted >= cycles_per_jiffy) {
no_idle_hz_jiffies++;
cycles_not_accounted -= cycles_per_jiffy;
per_cpu(process_times_cycles_accounted_cpu, cpu) += cycles_per_jiffy;
}
/* Account time to the idle process. */
account_steal_time(idle_task(cpu), jiffies_to_cputime(no_idle_hz_jiffies));
}
#endif
/* Update per-cpu stolen time. */
static void vmi_account_stolen_cycles(int cpu,
unsigned long long cur_real_cycles,
unsigned long long cur_avail_cycles)
{
long long stolen_cycles_not_accounted;
unsigned long stolen_jiffies = 0;
if (cur_real_cycles < cur_avail_cycles)
return;
stolen_cycles_not_accounted = cur_real_cycles - cur_avail_cycles -
per_cpu(stolen_cycles_accounted_cpu, cpu);
while (stolen_cycles_not_accounted >= cycles_per_jiffy) {
stolen_jiffies++;
stolen_cycles_not_accounted -= cycles_per_jiffy;
per_cpu(stolen_cycles_accounted_cpu, cpu) += cycles_per_jiffy;
}
/* HACK: pass NULL to force time onto cpustat->steal. */
account_steal_time(NULL, jiffies_to_cputime(stolen_jiffies));
}
/* Body of either IRQ0 interrupt handler (UP no local-APIC) or
* local-APIC LVTT interrupt handler (UP & local-APIC or SMP). */
static void vmi_local_timer_interrupt(int cpu)
{
unsigned long long cur_real_cycles, cur_process_times_cycles;
cur_real_cycles = read_real_cycles();
cur_process_times_cycles = read_available_cycles();
/* Update system wide (real) time state (xtime, jiffies). */
vmi_account_real_cycles(cur_real_cycles);
/* Update per-cpu process times. */
vmi_account_process_times_cycles(get_irq_regs(), cpu, cur_process_times_cycles);
/* Update time stolen from this cpu by the hypervisor. */
vmi_account_stolen_cycles(cpu, cur_real_cycles, cur_process_times_cycles);
}
#ifdef CONFIG_NO_IDLE_HZ
/* Must be called only from idle loop, with interrupts disabled. */
int vmi_stop_hz_timer(void)
{
/* Note that cpu_set, cpu_clear are (SMP safe) atomic on x86. */
unsigned long seq, next;
unsigned long long real_cycles_expiry;
int cpu = smp_processor_id();
int idle;
BUG_ON(!irqs_disabled());
if (sysctl_hz_timer != 0)
return 0;
cpu_set(cpu, nohz_cpu_mask);
smp_mb();
if (rcu_needs_cpu(cpu) || local_softirq_pending() ||
(next = next_timer_interrupt(), time_before_eq(next, jiffies))) {
cpu_clear(cpu, nohz_cpu_mask);
next = jiffies;
idle = 0;
} else
idle = 1;
/* Convert jiffies to the real cycle counter. */
do {
seq = read_seqbegin(&xtime_lock);
real_cycles_expiry = real_cycles_accounted_system +
(long)(next - jiffies) * cycles_per_jiffy;
} while (read_seqretry(&xtime_lock, seq));
/* This cpu is going idle. Disable the periodic alarm. */
if (idle) {
vmi_timer_ops.cancel_alarm(VMI_CYCLES_AVAILABLE);
per_cpu(idle_start_jiffies, cpu) = jiffies;
}
/* Set the real time alarm to expire at the next event. */
vmi_timer_ops.set_alarm(
VMI_ALARM_WIRING | VMI_ALARM_IS_ONESHOT | VMI_CYCLES_REAL,
real_cycles_expiry, 0);
return idle;
}
static void vmi_reenable_hz_timer(int cpu)
{
/* For /proc/vmi/info idle_hz stat. */
per_cpu(vmi_idle_no_hz_jiffies, cpu) += jiffies - per_cpu(idle_start_jiffies, cpu);
per_cpu(vmi_idle_no_hz_irqs, cpu)++;
/* Don't bother explicitly cancelling the one-shot alarm -- at
* worse we will receive a spurious timer interrupt. */
vmi_timer_ops.set_alarm(
VMI_ALARM_WIRING | VMI_ALARM_IS_PERIODIC | VMI_CYCLES_AVAILABLE,
per_cpu(process_times_cycles_accounted_cpu, cpu) + cycles_per_alarm,
cycles_per_alarm);
/* Indicate this cpu is no longer nohz idle. */
cpu_clear(cpu, nohz_cpu_mask);
}
/* Called from interrupt handlers when (local) HZ timer is disabled. */
void vmi_account_time_restart_hz_timer(void)
{
unsigned long long cur_real_cycles, cur_process_times_cycles;
int cpu = smp_processor_id();
BUG_ON(!irqs_disabled());
/* Account the time during which the HZ timer was disabled. */
cur_real_cycles = read_real_cycles();
cur_process_times_cycles = read_available_cycles();
/* Update system wide (real) time state (xtime, jiffies). */
vmi_account_real_cycles(cur_real_cycles);
/* Update per-cpu idle times. */
vmi_account_no_hz_idle_cycles(cpu, cur_process_times_cycles);
/* Update time stolen from this cpu by the hypervisor. */
vmi_account_stolen_cycles(cpu, cur_real_cycles, cur_process_times_cycles);
/* Reenable the hz timer. */
vmi_reenable_hz_timer(cpu);
}
#endif /* CONFIG_NO_IDLE_HZ */
/* UP (and no local-APIC) VMI-timer alarm interrupt handler.
* Handler for IRQ0. Not used when SMP or X86_LOCAL_APIC after
* APIC setup and setup_boot_vmi_alarm() is called. */
static irqreturn_t vmi_timer_interrupt(int irq, void *dev_id)
{
vmi_local_timer_interrupt(smp_processor_id());
return IRQ_HANDLED;
}
#ifdef CONFIG_X86_LOCAL_APIC
/* SMP VMI-timer alarm interrupt handler. Handler for LVTT vector.
* Also used in UP when CONFIG_X86_LOCAL_APIC.
* The wrapper code is from arch/i386/kernel/apic.c#smp_apic_timer_interrupt. */
void smp_apic_vmi_timer_interrupt(struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
int cpu = smp_processor_id();
/*
* the NMI deadlock-detector uses this.
*/
per_cpu(irq_stat,cpu).apic_timer_irqs++;
/*
* NOTE! We'd better ACK the irq immediately,
* because timer handling can be slow.
*/
ack_APIC_irq();
/*
* update_process_times() expects us to have done irq_enter().
* Besides, if we don't timer interrupts ignore the global
* interrupt lock, which is the WrongThing (tm) to do.
*/
irq_enter();
vmi_local_timer_interrupt(cpu);
irq_exit();
set_irq_regs(old_regs);
}
#endif /* CONFIG_X86_LOCAL_APIC */

View File

@ -37,9 +37,14 @@ SECTIONS
{
. = LOAD_OFFSET + LOAD_PHYSICAL_ADDR;
phys_startup_32 = startup_32 - LOAD_OFFSET;
.text.head : AT(ADDR(.text.head) - LOAD_OFFSET) {
_text = .; /* Text and read-only data */
*(.text.head)
} :text = 0x9090
/* read-only */
.text : AT(ADDR(.text) - LOAD_OFFSET) {
_text = .; /* Text and read-only data */
*(.text)
SCHED_TEXT
LOCK_TEXT

View File

@ -79,7 +79,12 @@ void __init trap_init_hook(void)
{
}
static struct irqaction irq0 = { timer_interrupt, IRQF_DISABLED, CPU_MASK_NONE, "timer", NULL, NULL};
static struct irqaction irq0 = {
.handler = timer_interrupt,
.flags = IRQF_DISABLED | IRQF_NOBALANCING,
.mask = CPU_MASK_NONE,
.name = "timer"
};
/**
* time_init_hook - do any specific initialisations for the system timer.
@ -90,6 +95,7 @@ static struct irqaction irq0 = { timer_interrupt, IRQF_DISABLED, CPU_MASK_NONE,
**/
void __init time_init_hook(void)
{
irq0.mask = cpumask_of_cpu(0);
setup_irq(0, &irq0);
}

View File

@ -56,15 +56,14 @@ static int reg_offset_vm86[] = {
#define VM86_REG_(x) (*(unsigned short *) \
(reg_offset_vm86[((unsigned)x)]+(u_char *) FPU_info))
/* These are dummy, fs and gs are not saved on the stack. */
#define ___FS ___ds
/* This dummy, gs is not saved on the stack. */
#define ___GS ___ds
static int reg_offset_pm[] = {
offsetof(struct info,___cs),
offsetof(struct info,___ds),
offsetof(struct info,___es),
offsetof(struct info,___FS),
offsetof(struct info,___fs),
offsetof(struct info,___GS),
offsetof(struct info,___ss),
offsetof(struct info,___ds)
@ -169,13 +168,10 @@ static long pm_address(u_char FPU_modrm, u_char segment,
switch ( segment )
{
/* fs and gs aren't used by the kernel, so they still have their
user-space values. */
case PREFIX_FS_-1:
/* N.B. - movl %seg, mem is a 2 byte write regardless of prefix */
savesegment(fs, addr->selector);
break;
/* gs isn't used by the kernel, so it still has its
user-space value. */
case PREFIX_GS_-1:
/* N.B. - movl %seg, mem is a 2 byte write regardless of prefix */
savesegment(gs, addr->selector);
break;
default:

View File

@ -48,9 +48,11 @@
#define status_word() \
((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top))
#define setcc(cc) ({ \
partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
static inline void setcc(int cc)
{
partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3);
partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3);
}
#ifdef PECULIAR_486
/* Default, this conveys no information, but an 80486 does it. */

View File

@ -101,7 +101,6 @@ extern void find_max_pfn(void);
extern void add_one_highpage_init(struct page *, int, int);
extern struct e820map e820;
extern unsigned long init_pg_tables_end;
extern unsigned long highend_pfn, highstart_pfn;
extern unsigned long max_low_pfn;
extern unsigned long totalram_pages;

View File

@ -46,17 +46,17 @@ int unregister_page_fault_notifier(struct notifier_block *nb)
}
EXPORT_SYMBOL_GPL(unregister_page_fault_notifier);
static inline int notify_page_fault(enum die_val val, const char *str,
struct pt_regs *regs, long err, int trap, int sig)
static inline int notify_page_fault(struct pt_regs *regs, long err)
{
struct die_args args = {
.regs = regs,
.str = str,
.str = "page fault",
.err = err,
.trapnr = trap,
.signr = sig
.trapnr = 14,
.signr = SIGSEGV
};
return atomic_notifier_call_chain(&notify_page_fault_chain, val, &args);
return atomic_notifier_call_chain(&notify_page_fault_chain,
DIE_PAGE_FAULT, &args);
}
/*
@ -327,8 +327,7 @@ fastcall void __kprobes do_page_fault(struct pt_regs *regs,
if (unlikely(address >= TASK_SIZE)) {
if (!(error_code & 0x0000000d) && vmalloc_fault(address) >= 0)
return;
if (notify_page_fault(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
SIGSEGV) == NOTIFY_STOP)
if (notify_page_fault(regs, error_code) == NOTIFY_STOP)
return;
/*
* Don't take the mm semaphore here. If we fixup a prefetch
@ -337,8 +336,7 @@ fastcall void __kprobes do_page_fault(struct pt_regs *regs,
goto bad_area_nosemaphore;
}
if (notify_page_fault(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
SIGSEGV) == NOTIFY_STOP)
if (notify_page_fault(regs, error_code) == NOTIFY_STOP)
return;
/* It's safe to allow irq's after cr2 has been saved and the vmalloc

View File

@ -62,6 +62,7 @@ static pmd_t * __init one_md_table_init(pgd_t *pgd)
#ifdef CONFIG_X86_PAE
pmd_table = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
paravirt_alloc_pd(__pa(pmd_table) >> PAGE_SHIFT);
set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT));
pud = pud_offset(pgd, 0);
if (pmd_table != pmd_offset(pud, 0))
@ -82,6 +83,7 @@ static pte_t * __init one_page_table_init(pmd_t *pmd)
{
if (pmd_none(*pmd)) {
pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
paravirt_alloc_pt(__pa(page_table) >> PAGE_SHIFT);
set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE));
if (page_table != pte_offset_kernel(pmd, 0))
BUG();
@ -345,6 +347,8 @@ static void __init pagetable_init (void)
/* Init entries of the first-level page table to the zero page */
for (i = 0; i < PTRS_PER_PGD; i++)
set_pgd(pgd_base + i, __pgd(__pa(empty_zero_page) | _PAGE_PRESENT));
#else
paravirt_alloc_pd(__pa(swapper_pg_dir) >> PAGE_SHIFT);
#endif
/* Enable PSE if available */

View File

@ -60,6 +60,7 @@ static struct page *split_large_page(unsigned long address, pgprot_t prot,
address = __pa(address);
addr = address & LARGE_PAGE_MASK;
pbase = (pte_t *)page_address(base);
paravirt_alloc_pt(page_to_pfn(base));
for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) {
set_pte(&pbase[i], pfn_pte(addr >> PAGE_SHIFT,
addr == address ? prot : ref_prot));
@ -172,6 +173,7 @@ __change_page_attr(struct page *page, pgprot_t prot)
if (!PageReserved(kpte_page)) {
if (cpu_has_pse && (page_private(kpte_page) == 0)) {
ClearPagePrivate(kpte_page);
paravirt_release_pt(page_to_pfn(kpte_page));
list_add(&kpte_page->lru, &df_list);
revert_page(kpte_page, address);
}

View File

@ -171,6 +171,8 @@ void __set_fixmap (enum fixed_addresses idx, unsigned long phys, pgprot_t flags)
void reserve_top_address(unsigned long reserve)
{
BUG_ON(fixmaps > 0);
printk(KERN_INFO "Reserving virtual address space above 0x%08x\n",
(int)-reserve);
#ifdef CONFIG_COMPAT_VDSO
BUG_ON(reserve != 0);
#else
@ -248,9 +250,15 @@ void pgd_ctor(void *pgd, struct kmem_cache *cache, unsigned long unused)
clone_pgd_range((pgd_t *)pgd + USER_PTRS_PER_PGD,
swapper_pg_dir + USER_PTRS_PER_PGD,
KERNEL_PGD_PTRS);
if (PTRS_PER_PMD > 1)
return;
/* must happen under lock */
paravirt_alloc_pd_clone(__pa(pgd) >> PAGE_SHIFT,
__pa(swapper_pg_dir) >> PAGE_SHIFT,
USER_PTRS_PER_PGD, PTRS_PER_PGD - USER_PTRS_PER_PGD);
pgd_list_add(pgd);
spin_unlock_irqrestore(&pgd_lock, flags);
}
@ -260,6 +268,7 @@ void pgd_dtor(void *pgd, struct kmem_cache *cache, unsigned long unused)
{
unsigned long flags; /* can be called from interrupt context */
paravirt_release_pd(__pa(pgd) >> PAGE_SHIFT);
spin_lock_irqsave(&pgd_lock, flags);
pgd_list_del(pgd);
spin_unlock_irqrestore(&pgd_lock, flags);
@ -277,13 +286,18 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
pmd_t *pmd = kmem_cache_alloc(pmd_cache, GFP_KERNEL);
if (!pmd)
goto out_oom;
paravirt_alloc_pd(__pa(pmd) >> PAGE_SHIFT);
set_pgd(&pgd[i], __pgd(1 + __pa(pmd)));
}
return pgd;
out_oom:
for (i--; i >= 0; i--)
kmem_cache_free(pmd_cache, (void *)__va(pgd_val(pgd[i])-1));
for (i--; i >= 0; i--) {
pgd_t pgdent = pgd[i];
void* pmd = (void *)__va(pgd_val(pgdent)-1);
paravirt_release_pd(__pa(pmd) >> PAGE_SHIFT);
kmem_cache_free(pmd_cache, pmd);
}
kmem_cache_free(pgd_cache, pgd);
return NULL;
}
@ -294,8 +308,12 @@ void pgd_free(pgd_t *pgd)
/* in the PAE case user pgd entries are overwritten before usage */
if (PTRS_PER_PMD > 1)
for (i = 0; i < USER_PTRS_PER_PGD; ++i)
kmem_cache_free(pmd_cache, (void *)__va(pgd_val(pgd[i])-1));
for (i = 0; i < USER_PTRS_PER_PGD; ++i) {
pgd_t pgdent = pgd[i];
void* pmd = (void *)__va(pgd_val(pgdent)-1);
paravirt_release_pd(__pa(pmd) >> PAGE_SHIFT);
kmem_cache_free(pmd_cache, pmd);
}
/* in the non-PAE case, free_pgtables() clears user pgd entries */
kmem_cache_free(pgd_cache, pgd);
}

View File

@ -24,7 +24,8 @@
#define CTR_IS_RESERVED(msrs,c) (msrs->counters[(c)].addr ? 1 : 0)
#define CTR_READ(l,h,msrs,c) do {rdmsr(msrs->counters[(c)].addr, (l), (h));} while (0)
#define CTR_WRITE(l,msrs,c) do {wrmsr(msrs->counters[(c)].addr, -(u32)(l), -1);} while (0)
#define CTR_32BIT_WRITE(l,msrs,c) \
do {wrmsr(msrs->counters[(c)].addr, -(u32)(l), 0);} while (0)
#define CTR_OVERFLOWED(n) (!((n) & (1U<<31)))
#define CTRL_IS_RESERVED(msrs,c) (msrs->controls[(c)].addr ? 1 : 0)
@ -79,7 +80,7 @@ static void ppro_setup_ctrs(struct op_msrs const * const msrs)
for (i = 0; i < NUM_COUNTERS; ++i) {
if (unlikely(!CTR_IS_RESERVED(msrs,i)))
continue;
CTR_WRITE(1, msrs, i);
CTR_32BIT_WRITE(1, msrs, i);
}
/* enable active counters */
@ -87,7 +88,7 @@ static void ppro_setup_ctrs(struct op_msrs const * const msrs)
if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs,i))) {
reset_value[i] = counter_config[i].count;
CTR_WRITE(counter_config[i].count, msrs, i);
CTR_32BIT_WRITE(counter_config[i].count, msrs, i);
CTRL_READ(low, high, msrs, i);
CTRL_CLEAR(low);
@ -116,7 +117,7 @@ static int ppro_check_ctrs(struct pt_regs * const regs,
CTR_READ(low, high, msrs, i);
if (CTR_OVERFLOWED(low)) {
oprofile_add_sample(regs, i);
CTR_WRITE(reset_value[i], msrs, i);
CTR_32BIT_WRITE(reset_value[i], msrs, i);
}
}

View File

@ -1,7 +1,7 @@
obj-y := i386.o init.o
obj-$(CONFIG_PCI_BIOS) += pcbios.o
obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o
obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o mmconfig-shared.o
obj-$(CONFIG_PCI_DIRECT) += direct.o
pci-y := fixup.o

Some files were not shown because too many files have changed in this diff Show More