/* * Intel 5100 Memory Controllers kernel module * * This file may be distributed under the terms of the * GNU General Public License. * * This module is based on the following document: * * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet * http://download.intel.com/design/chipsets/datashts/318378.pdf * * The intel 5100 has two independent channels. EDAC core currently * can not reflect this configuration so instead the chip-select * rows for each respective channel are laid out one after another, * the first half belonging to channel 0, the second half belonging * to channel 1. * * This driver is for DDR2 DIMMs, and it uses chip select to select among the * several ranks. However, instead of showing memories as ranks, it outputs * them as DIMM's. An internal table creates the association between ranks * and DIMM's. */ #include #include #include #include #include #include #include #include #include "edac_module.h" /* register addresses */ /* device 16, func 1 */ #define I5100_MC 0x40 /* Memory Control Register */ #define I5100_MC_SCRBEN_MASK (1 << 7) #define I5100_MC_SCRBDONE_MASK (1 << 4) #define I5100_MS 0x44 /* Memory Status Register */ #define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */ #define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */ #define I5100_TOLM 0x6c /* Top of Low Memory */ #define I5100_MIR0 0x80 /* Memory Interleave Range 0 */ #define I5100_MIR1 0x84 /* Memory Interleave Range 1 */ #define I5100_AMIR_0 0x8c /* Adjusted Memory Interleave Range 0 */ #define I5100_AMIR_1 0x90 /* Adjusted Memory Interleave Range 1 */ #define I5100_FERR_NF_MEM 0xa0 /* MC First Non Fatal Errors */ #define I5100_FERR_NF_MEM_M16ERR_MASK (1 << 16) #define I5100_FERR_NF_MEM_M15ERR_MASK (1 << 15) #define I5100_FERR_NF_MEM_M14ERR_MASK (1 << 14) #define I5100_FERR_NF_MEM_M12ERR_MASK (1 << 12) #define I5100_FERR_NF_MEM_M11ERR_MASK (1 << 11) #define I5100_FERR_NF_MEM_M10ERR_MASK (1 << 10) #define I5100_FERR_NF_MEM_M6ERR_MASK (1 << 6) #define I5100_FERR_NF_MEM_M5ERR_MASK (1 << 5) #define I5100_FERR_NF_MEM_M4ERR_MASK (1 << 4) #define I5100_FERR_NF_MEM_M1ERR_MASK (1 << 1) #define I5100_FERR_NF_MEM_ANY_MASK \ (I5100_FERR_NF_MEM_M16ERR_MASK | \ I5100_FERR_NF_MEM_M15ERR_MASK | \ I5100_FERR_NF_MEM_M14ERR_MASK | \ I5100_FERR_NF_MEM_M12ERR_MASK | \ I5100_FERR_NF_MEM_M11ERR_MASK | \ I5100_FERR_NF_MEM_M10ERR_MASK | \ I5100_FERR_NF_MEM_M6ERR_MASK | \ I5100_FERR_NF_MEM_M5ERR_MASK | \ I5100_FERR_NF_MEM_M4ERR_MASK | \ I5100_FERR_NF_MEM_M1ERR_MASK) #define I5100_NERR_NF_MEM 0xa4 /* MC Next Non-Fatal Errors */ #define I5100_EMASK_MEM 0xa8 /* MC Error Mask Register */ #define I5100_MEM0EINJMSK0 0x200 /* Injection Mask0 Register Channel 0 */ #define I5100_MEM1EINJMSK0 0x208 /* Injection Mask0 Register Channel 1 */ #define I5100_MEMXEINJMSK0_EINJEN (1 << 27) #define I5100_MEM0EINJMSK1 0x204 /* Injection Mask1 Register Channel 0 */ #define I5100_MEM1EINJMSK1 0x206 /* Injection Mask1 Register Channel 1 */ /* Device 19, Function 0 */ #define I5100_DINJ0 0x9a /* device 21 and 22, func 0 */ #define I5100_MTR_0 0x154 /* Memory Technology Registers 0-3 */ #define I5100_DMIR 0x15c /* DIMM Interleave Range */ #define I5100_VALIDLOG 0x18c /* Valid Log Markers */ #define I5100_NRECMEMA 0x190 /* Non-Recoverable Memory Error Log Reg A */ #define I5100_NRECMEMB 0x194 /* Non-Recoverable Memory Error Log Reg B */ #define I5100_REDMEMA 0x198 /* Recoverable Memory Data Error Log Reg A */ #define I5100_REDMEMB 0x19c /* Recoverable Memory Data Error Log Reg B */ #define I5100_RECMEMA 0x1a0 /* Recoverable Memory Error Log Reg A */ #define I5100_RECMEMB 0x1a4 /* Recoverable Memory Error Log Reg B */ #define I5100_MTR_4 0x1b0 /* Memory Technology Registers 4,5 */ /* bit field accessors */ static inline u32 i5100_mc_scrben(u32 mc) { return mc >> 7 & 1; } static inline u32 i5100_mc_errdeten(u32 mc) { return mc >> 5 & 1; } static inline u32 i5100_mc_scrbdone(u32 mc) { return mc >> 4 & 1; } static inline u16 i5100_spddata_rdo(u16 a) { return a >> 15 & 1; } static inline u16 i5100_spddata_sbe(u16 a) { return a >> 13 & 1; } static inline u16 i5100_spddata_busy(u16 a) { return a >> 12 & 1; } static inline u16 i5100_spddata_data(u16 a) { return a & ((1 << 8) - 1); } static inline u32 i5100_spdcmd_create(u32 dti, u32 ckovrd, u32 sa, u32 ba, u32 data, u32 cmd) { return ((dti & ((1 << 4) - 1)) << 28) | ((ckovrd & 1) << 27) | ((sa & ((1 << 3) - 1)) << 24) | ((ba & ((1 << 8) - 1)) << 16) | ((data & ((1 << 8) - 1)) << 8) | (cmd & 1); } static inline u16 i5100_tolm_tolm(u16 a) { return a >> 12 & ((1 << 4) - 1); } static inline u16 i5100_mir_limit(u16 a) { return a >> 4 & ((1 << 12) - 1); } static inline u16 i5100_mir_way1(u16 a) { return a >> 1 & 1; } static inline u16 i5100_mir_way0(u16 a) { return a & 1; } static inline u32 i5100_ferr_nf_mem_chan_indx(u32 a) { return a >> 28 & 1; } static inline u32 i5100_ferr_nf_mem_any(u32 a) { return a & I5100_FERR_NF_MEM_ANY_MASK; } static inline u32 i5100_nerr_nf_mem_any(u32 a) { return i5100_ferr_nf_mem_any(a); } static inline u32 i5100_dmir_limit(u32 a) { return a >> 16 & ((1 << 11) - 1); } static inline u32 i5100_dmir_rank(u32 a, u32 i) { return a >> (4 * i) & ((1 << 2) - 1); } static inline u16 i5100_mtr_present(u16 a) { return a >> 10 & 1; } static inline u16 i5100_mtr_ethrottle(u16 a) { return a >> 9 & 1; } static inline u16 i5100_mtr_width(u16 a) { return a >> 8 & 1; } static inline u16 i5100_mtr_numbank(u16 a) { return a >> 6 & 1; } static inline u16 i5100_mtr_numrow(u16 a) { return a >> 2 & ((1 << 2) - 1); } static inline u16 i5100_mtr_numcol(u16 a) { return a & ((1 << 2) - 1); } static inline u32 i5100_validlog_redmemvalid(u32 a) { return a >> 2 & 1; } static inline u32 i5100_validlog_recmemvalid(u32 a) { return a >> 1 & 1; } static inline u32 i5100_validlog_nrecmemvalid(u32 a) { return a & 1; } static inline u32 i5100_nrecmema_merr(u32 a) { return a >> 15 & ((1 << 5) - 1); } static inline u32 i5100_nrecmema_bank(u32 a) { return a >> 12 & ((1 << 3) - 1); } static inline u32 i5100_nrecmema_rank(u32 a) { return a >> 8 & ((1 << 3) - 1); } static inline u32 i5100_nrecmema_dm_buf_id(u32 a) { return a & ((1 << 8) - 1); } static inline u32 i5100_nrecmemb_cas(u32 a) { return a >> 16 & ((1 << 13) - 1); } static inline u32 i5100_nrecmemb_ras(u32 a) { return a & ((1 << 16) - 1); } static inline u32 i5100_redmemb_ecc_locator(u32 a) { return a & ((1 << 18) - 1); } static inline u32 i5100_recmema_merr(u32 a) { return i5100_nrecmema_merr(a); } static inline u32 i5100_recmema_bank(u32 a) { return i5100_nrecmema_bank(a); } static inline u32 i5100_recmema_rank(u32 a) { return i5100_nrecmema_rank(a); } static inline u32 i5100_recmemb_cas(u32 a) { return i5100_nrecmemb_cas(a); } static inline u32 i5100_recmemb_ras(u32 a) { return i5100_nrecmemb_ras(a); } /* some generic limits */ #define I5100_MAX_RANKS_PER_CHAN 6 #define I5100_CHANNELS 2 #define I5100_MAX_RANKS_PER_DIMM 4 #define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */ #define I5100_MAX_DIMM_SLOTS_PER_CHAN 4 #define I5100_MAX_RANK_INTERLEAVE 4 #define I5100_MAX_DMIRS 5 #define I5100_SCRUB_REFRESH_RATE (5 * 60 * HZ) struct i5100_priv { /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */ int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN]; /* * mainboard chip select map -- maps i5100 chip selects to * DIMM slot chip selects. In the case of only 4 ranks per * channel, the mapping is fairly obvious but not unique. * we map -1 -> NC and assume both channels use the same * map... * */ int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM]; /* memory interleave range */ struct { u64 limit; unsigned way[2]; } mir[I5100_CHANNELS]; /* adjusted memory interleave range register */ unsigned amir[I5100_CHANNELS]; /* dimm interleave range */ struct { unsigned rank[I5100_MAX_RANK_INTERLEAVE]; u64 limit; } dmir[I5100_CHANNELS][I5100_MAX_DMIRS]; /* memory technology registers... */ struct { unsigned present; /* 0 or 1 */ unsigned ethrottle; /* 0 or 1 */ unsigned width; /* 4 or 8 bits */ unsigned numbank; /* 2 or 3 lines */ unsigned numrow; /* 13 .. 16 lines */ unsigned numcol; /* 11 .. 12 lines */ } mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN]; u64 tolm; /* top of low memory in bytes */ unsigned ranksperchan; /* number of ranks per channel */ struct pci_dev *mc; /* device 16 func 1 */ struct pci_dev *einj; /* device 19 func 0 */ struct pci_dev *ch0mm; /* device 21 func 0 */ struct pci_dev *ch1mm; /* device 22 func 0 */ struct delayed_work i5100_scrubbing; int scrub_enable; /* Error injection */ u8 inject_channel; u8 inject_hlinesel; u8 inject_deviceptr1; u8 inject_deviceptr2; u16 inject_eccmask1; u16 inject_eccmask2; struct dentry *debugfs; }; static struct dentry *i5100_debugfs; /* map a rank/chan to a slot number on the mainboard */ static int i5100_rank_to_slot(const struct mem_ctl_info *mci, int chan, int rank) { const struct i5100_priv *priv = mci->pvt_info; int i; for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) { int j; const int numrank = priv->dimm_numrank[chan][i]; for (j = 0; j < numrank; j++) if (priv->dimm_csmap[i][j] == rank) return i * 2 + chan; } return -1; } static const char *i5100_err_msg(unsigned err) { static const char *merrs[] = { "unknown", /* 0 */ "uncorrectable data ECC on replay", /* 1 */ "unknown", /* 2 */ "unknown", /* 3 */ "aliased uncorrectable demand data ECC", /* 4 */ "aliased uncorrectable spare-copy data ECC", /* 5 */ "aliased uncorrectable patrol data ECC", /* 6 */ "unknown", /* 7 */ "unknown", /* 8 */ "unknown", /* 9 */ "non-aliased uncorrectable demand data ECC", /* 10 */ "non-aliased uncorrectable spare-copy data ECC", /* 11 */ "non-aliased uncorrectable patrol data ECC", /* 12 */ "unknown", /* 13 */ "correctable demand data ECC", /* 14 */ "correctable spare-copy data ECC", /* 15 */ "correctable patrol data ECC", /* 16 */ "unknown", /* 17 */ "SPD protocol error", /* 18 */ "unknown", /* 19 */ "spare copy initiated", /* 20 */ "spare copy completed", /* 21 */ }; unsigned i; for (i = 0; i < ARRAY_SIZE(merrs); i++) if (1 << i & err) return merrs[i]; return "none"; } /* convert csrow index into a rank (per channel -- 0..5) */ static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow) { const struct i5100_priv *priv = mci->pvt_info; return csrow % priv->ranksperchan; } /* convert csrow index into a channel (0..1) */ static int i5100_csrow_to_chan(const struct mem_ctl_info *mci, int csrow) { const struct i5100_priv *priv = mci->pvt_info; return csrow / priv->ranksperchan; } static void i5100_handle_ce(struct mem_ctl_info *mci, int chan, unsigned bank, unsigned rank, unsigned long syndrome, unsigned cas, unsigned ras, const char *msg) { char detail[80]; /* Form out message */ snprintf(detail, sizeof(detail), "bank %u, cas %u, ras %u\n", bank, cas, ras); edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, syndrome, chan, rank, -1, msg, detail); } static void i5100_handle_ue(struct mem_ctl_info *mci, int chan, unsigned bank, unsigned rank, unsigned long syndrome, unsigned cas, unsigned ras, const char *msg) { char detail[80]; /* Form out message */ snprintf(detail, sizeof(detail), "bank %u, cas %u, ras %u\n", bank, cas, ras); edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, syndrome, chan, rank, -1, msg, detail); } static void i5100_read_log(struct mem_ctl_info *mci, int chan, u32 ferr, u32 nerr) { struct i5100_priv *priv = mci->pvt_info; struct pci_dev *pdev = (chan) ? priv->ch1mm : priv->ch0mm; u32 dw; u32 dw2; unsigned syndrome = 0; unsigned ecc_loc = 0; unsigned merr; unsigned bank; unsigned rank; unsigned cas; unsigned ras; pci_read_config_dword(pdev, I5100_VALIDLOG, &dw); if (i5100_validlog_redmemvalid(dw)) { pci_read_config_dword(pdev, I5100_REDMEMA, &dw2); syndrome = dw2; pci_read_config_dword(pdev, I5100_REDMEMB, &dw2); ecc_loc = i5100_redmemb_ecc_locator(dw2); } if (i5100_validlog_recmemvalid(dw)) { const char *msg; pci_read_config_dword(pdev, I5100_RECMEMA, &dw2); merr = i5100_recmema_merr(dw2); bank = i5100_recmema_bank(dw2); rank = i5100_recmema_rank(dw2); pci_read_config_dword(pdev, I5100_RECMEMB, &dw2); cas = i5100_recmemb_cas(dw2); ras = i5100_recmemb_ras(dw2); /* FIXME: not really sure if this is what merr is... */ if (!merr) msg = i5100_err_msg(ferr); else msg = i5100_err_msg(nerr); i5100_handle_ce(mci, chan, bank, rank, syndrome, cas, ras, msg); } if (i5100_validlog_nrecmemvalid(dw)) { const char *msg; pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2); merr = i5100_nrecmema_merr(dw2); bank = i5100_nrecmema_bank(dw2); rank = i5100_nrecmema_rank(dw2); pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2); cas = i5100_nrecmemb_cas(dw2); ras = i5100_nrecmemb_ras(dw2); /* FIXME: not really sure if this is what merr is... */ if (!merr) msg = i5100_err_msg(ferr); else msg = i5100_err_msg(nerr); i5100_handle_ue(mci, chan, bank, rank, syndrome, cas, ras, msg); } pci_write_config_dword(pdev, I5100_VALIDLOG, dw); } static void i5100_check_error(struct mem_ctl_info *mci) { struct i5100_priv *priv = mci->pvt_info; u32 dw, dw2; pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw); if (i5100_ferr_nf_mem_any(dw)) { pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2); i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw), i5100_ferr_nf_mem_any(dw), i5100_nerr_nf_mem_any(dw2)); pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, dw2); } pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw); } /* The i5100 chipset will scrub the entire memory once, then * set a done bit. Continuous scrubbing is achieved by enqueing * delayed work to a workqueue, checking every few minutes if * the scrubbing has completed and if so reinitiating it. */ static void i5100_refresh_scrubbing(struct work_struct *work) { struct delayed_work *i5100_scrubbing = to_delayed_work(work); struct i5100_priv *priv = container_of(i5100_scrubbing, struct i5100_priv, i5100_scrubbing); u32 dw; pci_read_config_dword(priv->mc, I5100_MC, &dw); if (priv->scrub_enable) { pci_read_config_dword(priv->mc, I5100_MC, &dw); if (i5100_mc_scrbdone(dw)) { dw |= I5100_MC_SCRBEN_MASK; pci_write_config_dword(priv->mc, I5100_MC, dw); pci_read_config_dword(priv->mc, I5100_MC, &dw); } schedule_delayed_work(&(priv->i5100_scrubbing), I5100_SCRUB_REFRESH_RATE); } } /* * The bandwidth is based on experimentation, feel free to refine it. */ static int i5100_set_scrub_rate(struct mem_ctl_info *mci, u32 bandwidth) { struct i5100_priv *priv = mci->pvt_info; u32 dw; pci_read_config_dword(priv->mc, I5100_MC, &dw); if (bandwidth) { priv->scrub_enable = 1; dw |= I5100_MC_SCRBEN_MASK; schedule_delayed_work(&(priv->i5100_scrubbing), I5100_SCRUB_REFRESH_RATE); } else { priv->scrub_enable = 0; dw &= ~I5100_MC_SCRBEN_MASK; cancel_delayed_work(&(priv->i5100_scrubbing)); } pci_write_config_dword(priv->mc, I5100_MC, dw); pci_read_config_dword(priv->mc, I5100_MC, &dw); bandwidth = 5900000 * i5100_mc_scrben(dw); return bandwidth; } static int i5100_get_scrub_rate(struct mem_ctl_info *mci) { struct i5100_priv *priv = mci->pvt_info; u32 dw; pci_read_config_dword(priv->mc, I5100_MC, &dw); return 5900000 * i5100_mc_scrben(dw); } static struct pci_dev *pci_get_device_func(unsigned vendor, unsigned device, unsigned func) { struct pci_dev *ret = NULL; while (1) { ret = pci_get_device(vendor, device, ret); if (!ret) break; if (PCI_FUNC(ret->devfn) == func) break; } return ret; } static unsigned long i5100_npages(struct mem_ctl_info *mci, int csrow) { struct i5100_priv *priv = mci->pvt_info; const unsigned chan_rank = i5100_csrow_to_rank(mci, csrow); const unsigned chan = i5100_csrow_to_chan(mci, csrow); unsigned addr_lines; /* dimm present? */ if (!priv->mtr[chan][chan_rank].present) return 0ULL; addr_lines = I5100_DIMM_ADDR_LINES + priv->mtr[chan][chan_rank].numcol + priv->mtr[chan][chan_rank].numrow + priv->mtr[chan][chan_rank].numbank; return (unsigned long) ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE); } static void i5100_init_mtr(struct mem_ctl_info *mci) { struct i5100_priv *priv = mci->pvt_info; struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; int i; for (i = 0; i < I5100_CHANNELS; i++) { int j; struct pci_dev *pdev = mms[i]; for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) { const unsigned addr = (j < 4) ? I5100_MTR_0 + j * 2 : I5100_MTR_4 + (j - 4) * 2; u16 w; pci_read_config_word(pdev, addr, &w); priv->mtr[i][j].present = i5100_mtr_present(w); priv->mtr[i][j].ethrottle = i5100_mtr_ethrottle(w); priv->mtr[i][j].width = 4 + 4 * i5100_mtr_width(w); priv->mtr[i][j].numbank = 2 + i5100_mtr_numbank(w); priv->mtr[i][j].numrow = 13 + i5100_mtr_numrow(w); priv->mtr[i][j].numcol = 10 + i5100_mtr_numcol(w); } } } /* * FIXME: make this into a real i2c adapter (so that dimm-decode * will work)? */ static int i5100_read_spd_byte(const struct mem_ctl_info *mci, u8 ch, u8 slot, u8 addr, u8 *byte) { struct i5100_priv *priv = mci->pvt_info; u16 w; unsigned long et; pci_read_config_word(priv->mc, I5100_SPDDATA, &w); if (i5100_spddata_busy(w)) return -1; pci_write_config_dword(priv->mc, I5100_SPDCMD, i5100_spdcmd_create(0xa, 1, ch * 4 + slot, addr, 0, 0)); /* wait up to 100ms */ et = jiffies + HZ / 10; udelay(100); while (1) { pci_read_config_word(priv->mc, I5100_SPDDATA, &w); if (!i5100_spddata_busy(w)) break; udelay(100); } if (!i5100_spddata_rdo(w) || i5100_spddata_sbe(w)) return -1; *byte = i5100_spddata_data(w); return 0; } /* * fill dimm chip select map * * FIXME: * o not the only way to may chip selects to dimm slots * o investigate if there is some way to obtain this map from the bios */ static void i5100_init_dimm_csmap(struct mem_ctl_info *mci) { struct i5100_priv *priv = mci->pvt_info; int i; for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) { int j; for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++) priv->dimm_csmap[i][j] = -1; /* default NC */ } /* only 2 chip selects per slot... */ if (priv->ranksperchan == 4) { priv->dimm_csmap[0][0] = 0; priv->dimm_csmap[0][1] = 3; priv->dimm_csmap[1][0] = 1; priv->dimm_csmap[1][1] = 2; priv->dimm_csmap[2][0] = 2; priv->dimm_csmap[3][0] = 3; } else { priv->dimm_csmap[0][0] = 0; priv->dimm_csmap[0][1] = 1; priv->dimm_csmap[1][0] = 2; priv->dimm_csmap[1][1] = 3; priv->dimm_csmap[2][0] = 4; priv->dimm_csmap[2][1] = 5; } } static void i5100_init_dimm_layout(struct pci_dev *pdev, struct mem_ctl_info *mci) { struct i5100_priv *priv = mci->pvt_info; int i; for (i = 0; i < I5100_CHANNELS; i++) { int j; for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) { u8 rank; if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0) priv->dimm_numrank[i][j] = 0; else priv->dimm_numrank[i][j] = (rank & 3) + 1; } } i5100_init_dimm_csmap(mci); } static void i5100_init_interleaving(struct pci_dev *pdev, struct mem_ctl_info *mci) { u16 w; u32 dw; struct i5100_priv *priv = mci->pvt_info; struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; int i; pci_read_config_word(pdev, I5100_TOLM, &w); priv->tolm = (u64) i5100_tolm_tolm(w) * 256 * 1024 * 1024; pci_read_config_word(pdev, I5100_MIR0, &w); priv->mir[0].limit = (u64) i5100_mir_limit(w) << 28; priv->mir[0].way[1] = i5100_mir_way1(w); priv->mir[0].way[0] = i5100_mir_way0(w); pci_read_config_word(pdev, I5100_MIR1, &w); priv->mir[1].limit = (u64) i5100_mir_limit(w) << 28; priv->mir[1].way[1] = i5100_mir_way1(w); priv->mir[1].way[0] = i5100_mir_way0(w); pci_read_config_word(pdev, I5100_AMIR_0, &w); priv->amir[0] = w; pci_read_config_word(pdev, I5100_AMIR_1, &w); priv->amir[1] = w; for (i = 0; i < I5100_CHANNELS; i++) { int j; for (j = 0; j < 5; j++) { int k; pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw); priv->dmir[i][j].limit = (u64) i5100_dmir_limit(dw) << 28; for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++) priv->dmir[i][j].rank[k] = i5100_dmir_rank(dw, k); } } i5100_init_mtr(mci); } static void i5100_init_csrows(struct mem_ctl_info *mci) { int i; struct i5100_priv *priv = mci->pvt_info; for (i = 0; i < mci->tot_dimms; i++) { struct dimm_info *dimm; const unsigned long npages = i5100_npages(mci, i); const unsigned chan = i5100_csrow_to_chan(mci, i); const unsigned rank = i5100_csrow_to_rank(mci, i); if (!npages) continue; dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers, chan, rank, 0); dimm->nr_pages = npages; dimm->grain = 32; dimm->dtype = (priv->mtr[chan][rank].width == 4) ? DEV_X4 : DEV_X8; dimm->mtype = MEM_RDDR2; dimm->edac_mode = EDAC_SECDED; snprintf(dimm->label, sizeof(dimm->label), "DIMM%u", i5100_rank_to_slot(mci, chan, rank)); edac_dbg(2, "dimm channel %d, rank %d, size %ld\n", chan, rank, (long)PAGES_TO_MiB(npages)); } } /**************************************************************************** * Error injection routines ****************************************************************************/ static void i5100_do_inject(struct mem_ctl_info *mci) { struct i5100_priv *priv = mci->pvt_info; u32 mask0; u16 mask1; /* MEM[1:0]EINJMSK0 * 31 - ADDRMATCHEN * 29:28 - HLINESEL * 00 Reserved * 01 Lower half of cache line * 10 Upper half of cache line * 11 Both upper and lower parts of cache line * 27 - EINJEN * 25:19 - XORMASK1 for deviceptr1 * 9:5 - SEC2RAM for deviceptr2 * 4:0 - FIR2RAM for deviceptr1 */ mask0 = ((priv->inject_hlinesel & 0x3) << 28) | I5100_MEMXEINJMSK0_EINJEN | ((priv->inject_eccmask1 & 0xffff) << 10) | ((priv->inject_deviceptr2 & 0x1f) << 5) | (priv->inject_deviceptr1 & 0x1f); /* MEM[1:0]EINJMSK1 * 15:0 - XORMASK2 for deviceptr2 */ mask1 = priv->inject_eccmask2; if (priv->inject_channel == 0) { pci_write_config_dword(priv->mc, I5100_MEM0EINJMSK0, mask0); pci_write_config_word(priv->mc, I5100_MEM0EINJMSK1, mask1); } else { pci_write_config_dword(priv->mc, I5100_MEM1EINJMSK0, mask0); pci_write_config_word(priv->mc, I5100_MEM1EINJMSK1, mask1); } /* Error Injection Response Function * Intel 5100 Memory Controller Hub Chipset (318378) datasheet * hints about this register but carry no data about them. All * data regarding device 19 is based on experimentation and the * Intel 7300 Chipset Memory Controller Hub (318082) datasheet * which appears to be accurate for the i5100 in this area. * * The injection code don't work without setting this register. * The register needs to be flipped off then on else the hardware * will only preform the first injection. * * Stop condition bits 7:4 * 1010 - Stop after one injection * 1011 - Never stop injecting faults * * Start condition bits 3:0 * 1010 - Never start * 1011 - Start immediately */ pci_write_config_byte(priv->einj, I5100_DINJ0, 0xaa); pci_write_config_byte(priv->einj, I5100_DINJ0, 0xab); } #define to_mci(k) container_of(k, struct mem_ctl_info, dev) static ssize_t inject_enable_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) { struct device *dev = file->private_data; struct mem_ctl_info *mci = to_mci(dev); i5100_do_inject(mci); return count; } static const struct file_operations i5100_inject_enable_fops = { .open = simple_open, .write = inject_enable_write, .llseek = generic_file_llseek, }; static int i5100_setup_debugfs(struct mem_ctl_info *mci) { struct i5100_priv *priv = mci->pvt_info; if (!i5100_debugfs) return -ENODEV; priv->debugfs = edac_debugfs_create_dir_at(mci->bus->name, i5100_debugfs); if (!priv->debugfs) return -ENOMEM; edac_debugfs_create_x8("inject_channel", S_IRUGO | S_IWUSR, priv->debugfs, &priv->inject_channel); edac_debugfs_create_x8("inject_hlinesel", S_IRUGO | S_IWUSR, priv->debugfs, &priv->inject_hlinesel); edac_debugfs_create_x8("inject_deviceptr1", S_IRUGO | S_IWUSR, priv->debugfs, &priv->inject_deviceptr1); edac_debugfs_create_x8("inject_deviceptr2", S_IRUGO | S_IWUSR, priv->debugfs, &priv->inject_deviceptr2); edac_debugfs_create_x16("inject_eccmask1", S_IRUGO | S_IWUSR, priv->debugfs, &priv->inject_eccmask1); edac_debugfs_create_x16("inject_eccmask2", S_IRUGO | S_IWUSR, priv->debugfs, &priv->inject_eccmask2); edac_debugfs_create_file("inject_enable", S_IWUSR, priv->debugfs, &mci->dev, &i5100_inject_enable_fops); return 0; } static int i5100_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { int rc; struct mem_ctl_info *mci; struct edac_mc_layer layers[2]; struct i5100_priv *priv; struct pci_dev *ch0mm, *ch1mm, *einj; int ret = 0; u32 dw; int ranksperch; if (PCI_FUNC(pdev->devfn) != 1) return -ENODEV; rc = pci_enable_device(pdev); if (rc < 0) { ret = rc; goto bail; } /* ECC enabled? */ pci_read_config_dword(pdev, I5100_MC, &dw); if (!i5100_mc_errdeten(dw)) { printk(KERN_INFO "i5100_edac: ECC not enabled.\n"); ret = -ENODEV; goto bail_pdev; } /* figure out how many ranks, from strapped state of 48GB_Mode input */ pci_read_config_dword(pdev, I5100_MS, &dw); ranksperch = !!(dw & (1 << 8)) * 2 + 4; /* enable error reporting... */ pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw); dw &= ~I5100_FERR_NF_MEM_ANY_MASK; pci_write_config_dword(pdev, I5100_EMASK_MEM, dw); /* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */ ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_21, 0); if (!ch0mm) { ret = -ENODEV; goto bail_pdev; } rc = pci_enable_device(ch0mm); if (rc < 0) { ret = rc; goto bail_ch0; } /* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */ ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_22, 0); if (!ch1mm) { ret = -ENODEV; goto bail_disable_ch0; } rc = pci_enable_device(ch1mm); if (rc < 0) { ret = rc; goto bail_ch1; } layers[0].type = EDAC_MC_LAYER_CHANNEL; layers[0].size = 2; layers[0].is_virt_csrow = false; layers[1].type = EDAC_MC_LAYER_SLOT; layers[1].size = ranksperch; layers[1].is_virt_csrow = true; mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*priv)); if (!mci) { ret = -ENOMEM; goto bail_disable_ch1; } /* device 19, func 0, Error injection */ einj = pci_get_device_func(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_19, 0); if (!einj) { ret = -ENODEV; goto bail_einj; } rc = pci_enable_device(einj); if (rc < 0) { ret = rc; goto bail_disable_einj; } mci->pdev = &pdev->dev; priv = mci->pvt_info; priv->ranksperchan = ranksperch; priv->mc = pdev; priv->ch0mm = ch0mm; priv->ch1mm = ch1mm; priv->einj = einj; INIT_DELAYED_WORK(&(priv->i5100_scrubbing), i5100_refresh_scrubbing); /* If scrubbing was already enabled by the bios, start maintaining it */ pci_read_config_dword(pdev, I5100_MC, &dw); if (i5100_mc_scrben(dw)) { priv->scrub_enable = 1; schedule_delayed_work(&(priv->i5100_scrubbing), I5100_SCRUB_REFRESH_RATE); } i5100_init_dimm_layout(pdev, mci); i5100_init_interleaving(pdev, mci); mci->mtype_cap = MEM_FLAG_FB_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_SECDED; mci->mod_name = "i5100_edac.c"; mci->ctl_name = "i5100"; mci->dev_name = pci_name(pdev); mci->ctl_page_to_phys = NULL; mci->edac_check = i5100_check_error; mci->set_sdram_scrub_rate = i5100_set_scrub_rate; mci->get_sdram_scrub_rate = i5100_get_scrub_rate; priv->inject_channel = 0; priv->inject_hlinesel = 0; priv->inject_deviceptr1 = 0; priv->inject_deviceptr2 = 0; priv->inject_eccmask1 = 0; priv->inject_eccmask2 = 0; i5100_init_csrows(mci); /* this strange construction seems to be in every driver, dunno why */ switch (edac_op_state) { case EDAC_OPSTATE_POLL: case EDAC_OPSTATE_NMI: break; default: edac_op_state = EDAC_OPSTATE_POLL; break; } if (edac_mc_add_mc(mci)) { ret = -ENODEV; goto bail_scrub; } i5100_setup_debugfs(mci); return ret; bail_scrub: priv->scrub_enable = 0; cancel_delayed_work_sync(&(priv->i5100_scrubbing)); edac_mc_free(mci); bail_disable_einj: pci_disable_device(einj); bail_einj: pci_dev_put(einj); bail_disable_ch1: pci_disable_device(ch1mm); bail_ch1: pci_dev_put(ch1mm); bail_disable_ch0: pci_disable_device(ch0mm); bail_ch0: pci_dev_put(ch0mm); bail_pdev: pci_disable_device(pdev); bail: return ret; } static void i5100_remove_one(struct pci_dev *pdev) { struct mem_ctl_info *mci; struct i5100_priv *priv; mci = edac_mc_del_mc(&pdev->dev); if (!mci) return; priv = mci->pvt_info; edac_debugfs_remove_recursive(priv->debugfs); priv->scrub_enable = 0; cancel_delayed_work_sync(&(priv->i5100_scrubbing)); pci_disable_device(pdev); pci_disable_device(priv->ch0mm); pci_disable_device(priv->ch1mm); pci_disable_device(priv->einj); pci_dev_put(priv->ch0mm); pci_dev_put(priv->ch1mm); pci_dev_put(priv->einj); edac_mc_free(mci); } static const struct pci_device_id i5100_pci_tbl[] = { /* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) }, { 0, } }; MODULE_DEVICE_TABLE(pci, i5100_pci_tbl); static struct pci_driver i5100_driver = { .name = KBUILD_BASENAME, .probe = i5100_init_one, .remove = i5100_remove_one, .id_table = i5100_pci_tbl, }; static int __init i5100_init(void) { int pci_rc; i5100_debugfs = edac_debugfs_create_dir_at("i5100_edac", NULL); pci_rc = pci_register_driver(&i5100_driver); return (pci_rc < 0) ? pci_rc : 0; } static void __exit i5100_exit(void) { edac_debugfs_remove(i5100_debugfs); pci_unregister_driver(&i5100_driver); } module_init(i5100_init); module_exit(i5100_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR ("Arthur Jones "); MODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers"); /a> 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396
/*
 * Handle the memory map.
 * The functions here do the job until bootmem takes over.
 *
 *  Getting sanitize_e820_map() in sync with i386 version by applying change:
 *  -  Provisions for empty E820 memory regions (reported by certain BIOSes).
 *     Alex Achenbach <xela@slit.de>, December 2002.
 *  Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
 *
 */
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/kexec.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/pfn.h>
#include <linux/suspend.h>
#include <linux/firmware-map.h>

#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/e820.h>
#include <asm/proto.h>
#include <asm/setup.h>
#include <asm/trampoline.h>

/*
 * The e820 map is the map that gets modified e.g. with command line parameters
 * and that is also registered with modifications in the kernel resource tree
 * with the iomem_resource as parent.
 *
 * The e820_saved is directly saved after the BIOS-provided memory map is
 * copied. It doesn't get modified afterwards. It's registered for the
 * /sys/firmware/memmap interface.
 *
 * That memory map is not modified and is used as base for kexec. The kexec'd
 * kernel should get the same memory map as the firmware provides. Then the
 * user can e.g. boot the original kernel with mem=1G while still booting the
 * next kernel with full memory.
 */
struct e820map e820;
struct e820map e820_saved;

/* For PCI or other memory-mapped resources */
unsigned long pci_mem_start = 0xaeedbabe;
#ifdef CONFIG_PCI
EXPORT_SYMBOL(pci_mem_start);
#endif

/*
 * This function checks if any part of the range <start,end> is mapped
 * with type.
 */
int
e820_any_mapped(u64 start, u64 end, unsigned type)
{
	int i;

	for (i = 0; i < e820.nr_map; i++) {
		struct e820entry *ei = &e820.map[i];

		if (type && ei->type != type)
			continue;
		if (ei->addr >= end || ei->addr + ei->size <= start)
			continue;
		return 1;
	}
	return 0;
}
EXPORT_SYMBOL_GPL(e820_any_mapped);

/*
 * This function checks if the entire range <start,end> is mapped with type.
 *
 * Note: this function only works correct if the e820 table is sorted and
 * not-overlapping, which is the case
 */
int __init e820_all_mapped(u64 start, u64 end, unsigned type)
{
	int i;

	for (i = 0; i < e820.nr_map; i++) {
		struct e820entry *ei = &e820.map[i];

		if (type && ei->type != type)
			continue;
		/* is the region (part) in overlap with the current region ?*/
		if (ei->addr >= end || ei->addr + ei->size <= start)
			continue;

		/* if the region is at the beginning of <start,end> we move
		 * start to the end of the region since it's ok until there
		 */
		if (ei->addr <= start)
			start = ei->addr + ei->size;
		/*
		 * if start is now at or beyond end, we're done, full
		 * coverage
		 */
		if (start >= end)
			return 1;
	}
	return 0;
}

/*
 * Add a memory region to the kernel e820 map.
 */
void __init e820_add_region(u64 start, u64 size, int type)
{
	int x = e820.nr_map;

	if (x == ARRAY_SIZE(e820.map)) {
		printk(KERN_ERR "Ooops! Too many entries in the memory map!\n");
		return;
	}

	e820.map[x].addr = start;
	e820.map[x].size = size;
	e820.map[x].type = type;
	e820.nr_map++;
}

void __init e820_print_map(char *who)
{
	int i;

	for (i = 0; i < e820.nr_map; i++) {
		printk(KERN_INFO " %s: %016Lx - %016Lx ", who,
		       (unsigned long long) e820.map[i].addr,
		       (unsigned long long)
		       (e820.map[i].addr + e820.map[i].size));
		switch (e820.map[i].type) {
		case E820_RAM:
		case E820_RESERVED_KERN:
			printk(KERN_CONT "(usable)\n");
			break;
		case E820_RESERVED:
			printk(KERN_CONT "(reserved)\n");
			break;
		case E820_ACPI:
			printk(KERN_CONT "(ACPI data)\n");
			break;
		case E820_NVS:
			printk(KERN_CONT "(ACPI NVS)\n");
			break;
		case E820_UNUSABLE:
			printk("(unusable)\n");
			break;
		default:
			printk(KERN_CONT "type %u\n", e820.map[i].type);
			break;
		}
	}
}

/*
 * Sanitize the BIOS e820 map.
 *
 * Some e820 responses include overlapping entries. The following
 * replaces the original e820 map with a new one, removing overlaps,
 * and resolving conflicting memory types in favor of highest
 * numbered type.
 *
 * The input parameter biosmap points to an array of 'struct
 * e820entry' which on entry has elements in the range [0, *pnr_map)
 * valid, and which has space for up to max_nr_map entries.
 * On return, the resulting sanitized e820 map entries will be in
 * overwritten in the same location, starting at biosmap.
 *
 * The integer pointed to by pnr_map must be valid on entry (the
 * current number of valid entries located at biosmap) and will
 * be updated on return, with the new number of valid entries
 * (something no more than max_nr_map.)
 *
 * The return value from sanitize_e820_map() is zero if it
 * successfully 'sanitized' the map entries passed in, and is -1
 * if it did nothing, which can happen if either of (1) it was
 * only passed one map entry, or (2) any of the input map entries
 * were invalid (start + size < start, meaning that the size was
 * so big the described memory range wrapped around through zero.)
 *
 *	Visually we're performing the following
 *	(1,2,3,4 = memory types)...
 *
 *	Sample memory map (w/overlaps):
 *	   ____22__________________
 *	   ______________________4_
 *	   ____1111________________
 *	   _44_____________________
 *	   11111111________________
 *	   ____________________33__
 *	   ___________44___________
 *	   __________33333_________
 *	   ______________22________
 *	   ___________________2222_
 *	   _________111111111______
 *	   _____________________11_
 *	   _________________4______
 *
 *	Sanitized equivalent (no overlap):
 *	   1_______________________
 *	   _44_____________________
 *	   ___1____________________
 *	   ____22__________________
 *	   ______11________________
 *	   _________1______________
 *	   __________3_____________
 *	   ___________44___________
 *	   _____________33_________
 *	   _______________2________
 *	   ________________1_______
 *	   _________________4______
 *	   ___________________2____
 *	   ____________________33__
 *	   ______________________4_
 */

int __init sanitize_e820_map(struct e820entry *biosmap, int max_nr_map,
				int *pnr_map)
{
	struct change_member {
		struct e820entry *pbios; /* pointer to original bios entry */
		unsigned long long addr; /* address for this change point */
	};
	static struct change_member change_point_list[2*E820_X_MAX] __initdata;
	static struct change_member *change_point[2*E820_X_MAX] __initdata;
	static struct e820entry *overlap_list[E820_X_MAX] __initdata;
	static struct e820entry new_bios[E820_X_MAX] __initdata;
	struct change_member *change_tmp;
	unsigned long current_type, last_type;
	unsigned long long last_addr;
	int chgidx, still_changing;
	int overlap_entries;
	int new_bios_entry;
	int old_nr, new_nr, chg_nr;
	int i;

	/* if there's only one memory region, don't bother */
	if (*pnr_map < 2)
		return -1;

	old_nr = *pnr_map;
	BUG_ON(old_nr > max_nr_map);

	/* bail out if we find any unreasonable addresses in bios map */
	for (i = 0; i < old_nr; i++)
		if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr)
			return -1;

	/* create pointers for initial change-point information (for sorting) */
	for (i = 0; i < 2 * old_nr; i++)
		change_point[i] = &change_point_list[i];

	/* record all known change-points (starting and ending addresses),
	   omitting those that are for empty memory regions */
	chgidx = 0;
	for (i = 0; i < old_nr; i++)	{
		if (biosmap[i].size != 0) {
			change_point[chgidx]->addr = biosmap[i].addr;
			change_point[chgidx++]->pbios = &biosmap[i];
			change_point[chgidx]->addr = biosmap[i].addr +
				biosmap[i].size;
			change_point[chgidx++]->pbios = &biosmap[i];
		}
	}
	chg_nr = chgidx;

	/* sort change-point list by memory addresses (low -> high) */
	still_changing = 1;
	while (still_changing)	{
		still_changing = 0;
		for (i = 1; i < chg_nr; i++)  {
			unsigned long long curaddr, lastaddr;
			unsigned long long curpbaddr, lastpbaddr;

			curaddr = change_point[i]->addr;
			lastaddr = change_point[i - 1]->addr;
			curpbaddr = change_point[i]->pbios->addr;
			lastpbaddr = change_point[i - 1]->pbios->addr;

			/*
			 * swap entries, when:
			 *
			 * curaddr > lastaddr or
			 * curaddr == lastaddr and curaddr == curpbaddr and
			 * lastaddr != lastpbaddr
			 */
			if (curaddr < lastaddr ||
			    (curaddr == lastaddr && curaddr == curpbaddr &&
			     lastaddr != lastpbaddr)) {
				change_tmp = change_point[i];
				change_point[i] = change_point[i-1];
				change_point[i-1] = change_tmp;
				still_changing = 1;
			}
		}
	}

	/* create a new bios memory map, removing overlaps */
	overlap_entries = 0;	 /* number of entries in the overlap table */
	new_bios_entry = 0;	 /* index for creating new bios map entries */
	last_type = 0;		 /* start with undefined memory type */
	last_addr = 0;		 /* start with 0 as last starting address */

	/* loop through change-points, determining affect on the new bios map */
	for (chgidx = 0; chgidx < chg_nr; chgidx++) {
		/* keep track of all overlapping bios entries */
		if (change_point[chgidx]->addr ==
		    change_point[chgidx]->pbios->addr) {
			/*
			 * add map entry to overlap list (> 1 entry
			 * implies an overlap)
			 */
			overlap_list[overlap_entries++] =
				change_point[chgidx]->pbios;
		} else {
			/*
			 * remove entry from list (order independent,
			 * so swap with last)
			 */
			for (i = 0; i < overlap_entries; i++) {
				if (overlap_list[i] ==
				    change_point[chgidx]->pbios)
					overlap_list[i] =
						overlap_list[overlap_entries-1];
			}
			overlap_entries--;
		}
		/*
		 * if there are overlapping entries, decide which
		 * "type" to use (larger value takes precedence --
		 * 1=usable, 2,3,4,4+=unusable)
		 */
		current_type = 0;
		for (i = 0; i < overlap_entries; i++)
			if (overlap_list[i]->type > current_type)
				current_type = overlap_list[i]->type;
		/*
		 * continue building up new bios map based on this
		 * information
		 */
		if (current_type != last_type)	{
			if (last_type != 0)	 {
				new_bios[new_bios_entry].size =
					change_point[chgidx]->addr - last_addr;
				/*
				 * move forward only if the new size
				 * was non-zero
				 */
				if (new_bios[new_bios_entry].size != 0)
					/*
					 * no more space left for new
					 * bios entries ?
					 */
					if (++new_bios_entry >= max_nr_map)
						break;
			}
			if (current_type != 0)	{
				new_bios[new_bios_entry].addr =
					change_point[chgidx]->addr;
				new_bios[new_bios_entry].type = current_type;
				last_addr = change_point[chgidx]->addr;
			}
			last_type = current_type;
		}
	}
	/* retain count for new bios entries */
	new_nr = new_bios_entry;

	/* copy new bios mapping into original location */
	memcpy(biosmap, new_bios, new_nr * sizeof(struct e820entry));
	*pnr_map = new_nr;

	return 0;
}

static int __init __append_e820_map(struct e820entry *biosmap, int nr_map)
{
	while (nr_map) {
		u64 start = biosmap->addr;
		u64 size = biosmap->size;
		u64 end = start + size;
		u32 type = biosmap->type;

		/* Overflow in 64 bits? Ignore the memory map. */
		if (start > end)
			return -1;

		e820_add_region(start, size, type);

		biosmap++;
		nr_map--;
	}
	return 0;
}

/*
 * Copy the BIOS e820 map into a safe place.
 *
 * Sanity-check it while we're at it..
 *
 * If we're lucky and live on a modern system, the setup code
 * will have given us a memory map that we can use to properly
 * set up memory.  If we aren't, we'll fake a memory map.
 */
static int __init append_e820_map(struct e820entry *biosmap, int nr_map)
{
	/* Only one memory region (or negative)? Ignore it */
	if (nr_map < 2)
		return -1;

	return __append_e820_map(biosmap, nr_map);
}

static u64 __init e820_update_range_map(struct e820map *e820x, u64 start,
					u64 size, unsigned old_type,
					unsigned new_type)
{
	int i;
	u64 real_updated_size = 0;

	BUG_ON(old_type == new_type);

	if (size > (ULLONG_MAX - start))
		size = ULLONG_MAX - start;

	for (i = 0; i < e820.nr_map; i++) {
		struct e820entry *ei = &e820x->map[i];
		u64 final_start, final_end;
		if (ei->type != old_type)
			continue;
		/* totally covered? */
		if (ei->addr >= start &&
		    (ei->addr + ei->size) <= (start + size)) {
			ei->type = new_type;
			real_updated_size += ei->size;
			continue;
		}
		/* partially covered */
		final_start = max(start, ei->addr);
		final_end = min(start + size, ei->addr + ei->size);
		if (final_start >= final_end)
			continue;
		e820_add_region(final_start, final_end - final_start,
					 new_type);
		real_updated_size += final_end - final_start;

		ei->size -= final_end - final_start;
		if (ei->addr < final_start)
			continue;
		ei->addr = final_end;
	}
	return real_updated_size;
}

u64 __init e820_update_range(u64 start, u64 size, unsigned old_type,
			     unsigned new_type)
{
	return e820_update_range_map(&e820, start, size, old_type, new_type);
}

static u64 __init e820_update_range_saved(u64 start, u64 size,
					  unsigned old_type, unsigned new_type)
{
	return e820_update_range_map(&e820_saved, start, size, old_type,
				     new_type);
}

/* make e820 not cover the range */
u64 __init e820_remove_range(u64 start, u64 size, unsigned old_type,
			     int checktype)
{
	int i;
	u64 real_removed_size = 0;

	if (size > (ULLONG_MAX - start))
		size = ULLONG_MAX - start;

	for (i = 0; i < e820.nr_map; i++) {
		struct e820entry *ei = &e820.map[i];
		u64 final_start, final_end;

		if (checktype && ei->type != old_type)
			continue;
		/* totally covered? */
		if (ei->addr >= start &&
		    (ei->addr + ei->size) <= (start + size)) {
			real_removed_size += ei->size;
			memset(ei, 0, sizeof(struct e820entry));
			continue;
		}
		/* partially covered */
		final_start = max(start, ei->addr);
		final_end = min(start + size, ei->addr + ei->size);
		if (final_start >= final_end)
			continue;
		real_removed_size += final_end - final_start;

		ei->size -= final_end - final_start;
		if (ei->addr < final_start)
			continue;
		ei->addr = final_end;
	}
	return real_removed_size;
}

void __init update_e820(void)
{
	int nr_map;

	nr_map = e820.nr_map;
	if (sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &nr_map))
		return;
	e820.nr_map = nr_map;
	printk(KERN_INFO "modified physical RAM map:\n");
	e820_print_map("modified");
}
static void __init update_e820_saved(void)
{
	int nr_map;

	nr_map = e820_saved.nr_map;
	if (sanitize_e820_map(e820_saved.map, ARRAY_SIZE(e820_saved.map), &nr_map))
		return;
	e820_saved.nr_map = nr_map;
}
#define MAX_GAP_END 0x100000000ull
/*
 * Search for a gap in the e820 memory space from start_addr to end_addr.
 */
__init int e820_search_gap(unsigned long *gapstart, unsigned long *gapsize,
		unsigned long start_addr, unsigned long long end_addr)
{
	unsigned long long last;
	int i = e820.nr_map;
	int found = 0;

	last = (end_addr && end_addr < MAX_GAP_END) ? end_addr : MAX_GAP_END;

	while (--i >= 0) {
		unsigned long long start = e820.map[i].addr;
		unsigned long long end = start + e820.map[i].size;

		if (end < start_addr)
			continue;

		/*
		 * Since "last" is at most 4GB, we know we'll
		 * fit in 32 bits if this condition is true
		 */
		if (last > end) {
			unsigned long gap = last - end;

			if (gap >= *gapsize) {
				*gapsize = gap;
				*gapstart = end;
				found = 1;
			}
		}
		if (start < last)
			last = start;
	}
	return found;
}

/*
 * Search for the biggest gap in the low 32 bits of the e820
 * memory space.  We pass this space to PCI to assign MMIO resources
 * for hotplug or unconfigured devices in.
 * Hopefully the BIOS let enough space left.
 */
__init void e820_setup_gap(void)
{
	unsigned long gapstart, gapsize, round;
	int found;

	gapstart = 0x10000000;
	gapsize = 0x400000;
	found  = e820_search_gap(&gapstart, &gapsize, 0, MAX_GAP_END);

#ifdef CONFIG_X86_64
	if (!found) {
		gapstart = (max_pfn << PAGE_SHIFT) + 1024*1024;
		printk(KERN_ERR "PCI: Warning: Cannot find a gap in the 32bit "
		       "address range\n"
		       KERN_ERR "PCI: Unassigned devices with 32bit resource "
		       "registers may break!\n");
	}
#endif

	/*
	 * See how much we want to round up: start off with
	 * rounding to the next 1MB area.
	 */
	round = 0x100000;
	while ((gapsize >> 4) > round)
		round += round;
	/* Fun with two's complement */
	pci_mem_start = (gapstart + round) & -round;

	printk(KERN_INFO
	       "Allocating PCI resources starting at %lx (gap: %lx:%lx)\n",
	       pci_mem_start, gapstart, gapsize);
}

/**
 * Because of the size limitation of struct boot_params, only first
 * 128 E820 memory entries are passed to kernel via
 * boot_params.e820_map, others are passed via SETUP_E820_EXT node of
 * linked list of struct setup_data, which is parsed here.
 */
void __init parse_e820_ext(struct setup_data *sdata, unsigned long pa_data)
{
	u32 map_len;
	int entries;
	struct e820entry *extmap;

	entries = sdata->len / sizeof(struct e820entry);
	map_len = sdata->len + sizeof(struct setup_data);
	if (map_len > PAGE_SIZE)
		sdata = early_ioremap(pa_data, map_len);
	extmap = (struct e820entry *)(sdata->data);
	__append_e820_map(extmap, entries);
	sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
	if (map_len > PAGE_SIZE)
		early_iounmap(sdata, map_len);
	printk(KERN_INFO "extended physical RAM map:\n");
	e820_print_map("extended");
}

#if defined(CONFIG_X86_64) || \
	(defined(CONFIG_X86_32) && defined(CONFIG_HIBERNATION))
/**
 * Find the ranges of physical addresses that do not correspond to
 * e820 RAM areas and mark the corresponding pages as nosave for
 * hibernation (32 bit) or software suspend and suspend to RAM (64 bit).
 *
 * This function requires the e820 map to be sorted and without any
 * overlapping entries and assumes the first e820 area to be RAM.
 */
void __init e820_mark_nosave_regions(unsigned long limit_pfn)
{
	int i;
	unsigned long pfn;

	pfn = PFN_DOWN(e820.map[0].addr + e820.map[0].size);
	for (i = 1; i < e820.nr_map; i++) {
		struct e820entry *ei = &e820.map[i];

		if (pfn < PFN_UP(ei->addr))
			register_nosave_region(pfn, PFN_UP(ei->addr));

		pfn = PFN_DOWN(ei->addr + ei->size);
		if (ei->type != E820_RAM && ei->type != E820_RESERVED_KERN)
			register_nosave_region(PFN_UP(ei->addr), pfn);

		if (pfn >= limit_pfn)
			break;
	}
}
#endif

#ifdef CONFIG_HIBERNATION
/**
 * Mark ACPI NVS memory region, so that we can save/restore it during
 * hibernation and the subsequent resume.
 */
static int __init e820_mark_nvs_memory(void)
{
	int i;

	for (i = 0; i < e820.nr_map; i++) {
		struct e820entry *ei = &e820.map[i];

		if (ei->type == E820_NVS)
			hibernate_nvs_register(ei->addr, ei->size);
	}

	return 0;
}
core_initcall(e820_mark_nvs_memory);
#endif

/*
 * Early reserved memory areas.
 */
#define MAX_EARLY_RES 20

struct early_res {
	u64 start, end;
	char name[16];
	char overlap_ok;
};
static struct early_res early_res[MAX_EARLY_RES] __initdata = {
	{ 0, PAGE_SIZE, "BIOS data page" },	/* BIOS data page */
	{}
};

static int __init find_overlapped_early(u64 start, u64 end)
{
	int i;
	struct early_res *r;

	for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
		r = &early_res[i];
		if (end > r->start && start < r->end)
			break;
	}

	return i;
}

/*
 * Drop the i-th range from the early reservation map,
 * by copying any higher ranges down one over it, and
 * clearing what had been the last slot.
 */
static void __init drop_range(int i)
{
	int j;

	for (j = i + 1; j < MAX_EARLY_RES && early_res[j].end; j++)
		;

	memmove(&early_res[i], &early_res[i + 1],
	       (j - 1 - i) * sizeof(struct early_res));

	early_res[j - 1].end = 0;
}

/*
 * Split any existing ranges that:
 *  1) are marked 'overlap_ok', and
 *  2) overlap with the stated range [start, end)
 * into whatever portion (if any) of the existing range is entirely
 * below or entirely above the stated range.  Drop the portion
 * of the existing range that overlaps with the stated range,
 * which will allow the caller of this routine to then add that
 * stated range without conflicting with any existing range.
 */
static void __init drop_overlaps_that_are_ok(u64 start, u64 end)
{
	int i;
	struct early_res *r;
	u64 lower_start, lower_end;
	u64 upper_start, upper_end;
	char name[16];

	for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
		r = &early_res[i];

		/* Continue past non-overlapping ranges */
		if (end <= r->start || start >= r->end)
			continue;

		/*
		 * Leave non-ok overlaps as is; let caller
		 * panic "Overlapping early reservations"
		 * when it hits this overlap.
		 */
		if (!r->overlap_ok)
			return;

		/*
		 * We have an ok overlap.  We will drop it from the early
		 * reservation map, and add back in any non-overlapping
		 * portions (lower or upper) as separate, overlap_ok,
		 * non-overlapping ranges.
		 */

		/* 1. Note any non-overlapping (lower or upper) ranges. */
		strncpy(name, r->name, sizeof(name) - 1);

		lower_start = lower_end = 0;
		upper_start = upper_end = 0;
		if (r->start < start) {
		 	lower_start = r->start;
			lower_end = start;
		}
		if (r->end > end) {
			upper_start = end;
			upper_end = r->end;
		}

		/* 2. Drop the original ok overlapping range */
		drop_range(i);

		i--;		/* resume for-loop on copied down entry */

		/* 3. Add back in any non-overlapping ranges. */
		if (lower_end)
			reserve_early_overlap_ok(lower_start, lower_end, name);
		if (upper_end)
			reserve_early_overlap_ok(upper_start, upper_end, name);
	}
}

static void __init __reserve_early(u64 start, u64 end, char *name,
						int overlap_ok)
{
	int i;
	struct early_res *r;

	i = find_overlapped_early(start, end);
	if (i >= MAX_EARLY_RES)
		panic("Too many early reservations");
	r = &early_res[i];
	if (r->end)
		panic("Overlapping early reservations "
		      "%llx-%llx %s to %llx-%llx %s\n",
		      start, end - 1, name?name:"", r->start,
		      r->end - 1, r->name);
	r->start = start;
	r->end = end;
	r->overlap_ok = overlap_ok;
	if (name)
		strncpy(r->name, name, sizeof(r->name) - 1);
}

/*
 * A few early reservtations come here.
 *
 * The 'overlap_ok' in the name of this routine does -not- mean it
 * is ok for these reservations to overlap an earlier reservation.
 * Rather it means that it is ok for subsequent reservations to
 * overlap this one.
 *
 * Use this entry point to reserve early ranges when you are doing
 * so out of "Paranoia", reserving perhaps more memory than you need,
 * just in case, and don't mind a subsequent overlapping reservation
 * that is known to be needed.
 *
 * The drop_overlaps_that_are_ok() call here isn't really needed.
 * It would be needed if we had two colliding 'overlap_ok'
 * reservations, so that the second such would not panic on the
 * overlap with the first.  We don't have any such as of this
 * writing, but might as well tolerate such if it happens in
 * the future.
 */
void __init reserve_early_overlap_ok(u64 start, u64 end, char *name)
{
	drop_overlaps_that_are_ok(start, end);
	__reserve_early(start, end, name, 1);
}

/*
 * Most early reservations come here.
 *
 * We first have drop_overlaps_that_are_ok() drop any pre-existing
 * 'overlap_ok' ranges, so that we can then reserve this memory
 * range without risk of panic'ing on an overlapping overlap_ok
 * early reservation.
 */
void __init reserve_early(u64 start, u64 end, char *name)
{
	drop_overlaps_that_are_ok(start, end);
	__reserve_early(start, end, name, 0);
}

void __init free_early(u64 start, u64 end)
{
	struct early_res *r;
	int i;

	i = find_overlapped_early(start, end);
	r = &early_res[i];
	if (i >= MAX_EARLY_RES || r->end != end || r->start != start)
		panic("free_early on not reserved area: %llx-%llx!",
			 start, end - 1);

	drop_range(i);
}

void __init early_res_to_bootmem(u64 start, u64 end)
{
	int i, count;
	u64 final_start, final_end;

	count  = 0;
	for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++)
		count++;

	printk(KERN_INFO "(%d early reservations) ==> bootmem [%010llx - %010llx]\n",
			 count, start, end);
	for (i = 0; i < count; i++) {
		struct early_res *r = &early_res[i];
		printk(KERN_INFO "  #%d [%010llx - %010llx] %16s", i,
			r->start, r->end, r->name);
		final_start = max(start, r->start);
		final_end = min(end, r->end);
		if (final_start >= final_end) {
			printk(KERN_CONT "\n");
			continue;
		}
		printk(KERN_CONT " ==> [%010llx - %010llx]\n",
			final_start, final_end);
		reserve_bootmem_generic(final_start, final_end - final_start,
				BOOTMEM_DEFAULT);
	}
}

/* Check for already reserved areas */
static inline int __init bad_addr(u64 *addrp, u64 size, u64 align)
{
	int i;
	u64 addr = *addrp;
	int changed = 0;
	struct early_res *r;
again:
	i = find_overlapped_early(addr, addr + size);
	r = &early_res[i];
	if (i < MAX_EARLY_RES && r->end) {
		*addrp = addr = round_up(r->end, align);
		changed = 1;
		goto again;
	}
	return changed;
}

/* Check for already reserved areas */
static inline int __init bad_addr_size(u64 *addrp, u64 *sizep, u64 align)
{
	int i;
	u64 addr = *addrp, last;
	u64 size = *sizep;
	int changed = 0;
again:
	last = addr + size;
	for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
		struct early_res *r = &early_res[i];
		if (last > r->start && addr < r->start) {
			size = r->start - addr;
			changed = 1;
			goto again;
		}
		if (last > r->end && addr < r->end) {
			addr = round_up(r->end, align);
			size = last - addr;
			changed = 1;
			goto again;
		}
		if (last <= r->end && addr >= r->start) {
			(*sizep)++;
			return 0;
		}
	}
	if (changed) {
		*addrp = addr;
		*sizep = size;
	}
	return changed;
}

/*
 * Find a free area with specified alignment in a specific range.
 */
u64 __init find_e820_area(u64 start, u64 end, u64 size, u64 align)
{
	int i;

	for (i = 0; i < e820.nr_map; i++) {
		struct e820entry *ei = &e820.map[i];
		u64 addr, last;
		u64 ei_last;

		if (ei->type != E820_RAM)
			continue;
		addr = round_up(ei->addr, align);
		ei_last = ei->addr + ei->size;
		if (addr < start)
			addr = round_up(start, align);
		if (addr >= ei_last)
			continue;
		while (bad_addr(&addr, size, align) && addr+size <= ei_last)
			;
		last = addr + size;
		if (last > ei_last)
			continue;
		if (last > end)
			continue;
		return addr;
	}
	return -1ULL;
}

/*
 * Find next free range after *start
 */
u64 __init find_e820_area_size(u64 start, u64 *sizep, u64 align)
{
	int i;

	for (i = 0; i < e820.nr_map; i++) {
		struct e820entry *ei = &e820.map[i];
		u64 addr, last;
		u64 ei_last;

		if (ei->type != E820_RAM)
			continue;
		addr = round_up(ei->addr, align);
		ei_last = ei->addr + ei->size;
		if (addr < start)
			addr = round_up(start, align);
		if (addr >= ei_last)
			continue;
		*sizep = ei_last - addr;
		while (bad_addr_size(&addr, sizep, align) &&
			addr + *sizep <= ei_last)
			;
		last = addr + *sizep;
		if (last > ei_last)
			continue;
		return addr;
	}
	return -1UL;

}

/*
 * pre allocated 4k and reserved it in e820
 */
u64 __init early_reserve_e820(u64 startt, u64 sizet, u64 align)
{
	u64 size = 0;
	u64 addr;
	u64 start;

	start = startt;
	while (size < sizet)
		start = find_e820_area_size(start, &size, align);

	if (size < sizet)
		return 0;

	addr = round_down(start + size - sizet, align);
	e820_update_range(addr, sizet, E820_RAM, E820_RESERVED);
	e820_update_range_saved(addr, sizet, E820_RAM, E820_RESERVED);
	printk(KERN_INFO "update e820 for early_reserve_e820\n");
	update_e820();
	update_e820_saved();

	return addr;
}

#ifdef CONFIG_X86_32
# ifdef CONFIG_X86_PAE
#  define MAX_ARCH_PFN		(1ULL<<(36-PAGE_SHIFT))
# else
#  define MAX_ARCH_PFN		(1ULL<<(32-PAGE_SHIFT))
# endif
#else /* CONFIG_X86_32 */
# define MAX_ARCH_PFN MAXMEM>>PAGE_SHIFT
#endif

/*
 * Find the highest page frame number we have available
 */
static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type)
{
	int i;
	unsigned long last_pfn = 0;
	unsigned long max_arch_pfn = MAX_ARCH_PFN;

	for (i = 0; i < e820.nr_map; i++) {
		struct e820entry *ei = &e820.map[i];
		unsigned long start_pfn;
		unsigned long end_pfn;

		if (ei->type != type)
			continue;

		start_pfn = ei->addr >> PAGE_SHIFT;
		end_pfn = (ei->addr + ei->size) >> PAGE_SHIFT;

		if (start_pfn >= limit_pfn)
			continue;
		if (end_pfn > limit_pfn) {
			last_pfn = limit_pfn;
			break;
		}
		if (end_pfn > last_pfn)
			last_pfn = end_pfn;
	}

	if (last_pfn > max_arch_pfn)
		last_pfn = max_arch_pfn;

	printk(KERN_INFO "last_pfn = %#lx max_arch_pfn = %#lx\n",
			 last_pfn, max_arch_pfn);
	return last_pfn;
}
unsigned long __init e820_end_of_ram_pfn(void)
{
	return e820_end_pfn(MAX_ARCH_PFN, E820_RAM);
}

unsigned long __init e820_end_of_low_ram_pfn(void)
{
	return e820_end_pfn(1UL<<(32 - PAGE_SHIFT), E820_RAM);
}
/*
 * Finds an active region in the address range from start_pfn to last_pfn and
 * returns its range in ei_startpfn and ei_endpfn for the e820 entry.
 */
int __init e820_find_active_region(const struct e820entry *ei,
				  unsigned long start_pfn,
				  unsigned long last_pfn,
				  unsigned long *ei_startpfn,
				  unsigned long *ei_endpfn)
{
	u64 align = PAGE_SIZE;

	*ei_startpfn = round_up(ei->addr, align) >> PAGE_SHIFT;
	*ei_endpfn = round_down(ei->addr + ei->size, align) >> PAGE_SHIFT;

	/* Skip map entries smaller than a page */
	if (*ei_startpfn >= *ei_endpfn)
		return 0;

	/* Skip if map is outside the node */
	if (ei->type != E820_RAM || *ei_endpfn <= start_pfn ||
				    *ei_startpfn >= last_pfn)
		return 0;

	/* Check for overlaps */
	if (*ei_startpfn < start_pfn)
		*ei_startpfn = start_pfn;
	if (*ei_endpfn > last_pfn)
		*ei_endpfn = last_pfn;

	return 1;
}

/* Walk the e820 map and register active regions within a node */
void __init e820_register_active_regions(int nid, unsigned long start_pfn,
					 unsigned long last_pfn)
{
	unsigned long ei_startpfn;
	unsigned long ei_endpfn;
	int i;

	for (i = 0; i < e820.nr_map; i++)
		if (e820_find_active_region(&e820.map[i],
					    start_pfn, last_pfn,
					    &ei_startpfn, &ei_endpfn))
			add_active_range(nid, ei_startpfn, ei_endpfn);
}

/*
 * Find the hole size (in bytes) in the memory range.
 * @start: starting address of the memory range to scan
 * @end: ending address of the memory range to scan
 */
u64 __init e820_hole_size(u64 start, u64 end)
{
	unsigned long start_pfn = start >> PAGE_SHIFT;
	unsigned long last_pfn = end >> PAGE_SHIFT;
	unsigned long ei_startpfn, ei_endpfn, ram = 0;
	int i;

	for (i = 0; i < e820.nr_map; i++) {
		if (e820_find_active_region(&e820.map[i],
					    start_pfn, last_pfn,
					    &ei_startpfn, &ei_endpfn))
			ram += ei_endpfn - ei_startpfn;
	}
	return end - start - ((u64)ram << PAGE_SHIFT);
}

static void early_panic(char *msg)
{
	early_printk(msg);
	panic(msg);
}

static int userdef __initdata;

/* "mem=nopentium" disables the 4MB page tables. */
static int __init parse_memopt(char *p)
{
	u64 mem_size;

	if (!p)
		return -EINVAL;

#ifdef CONFIG_X86_32
	if (!strcmp(p, "nopentium")) {
		setup_clear_cpu_cap(X86_FEATURE_PSE);
		return 0;
	}
#endif

	userdef = 1;
	mem_size = memparse(p, &p);
	e820_remove_range(mem_size, ULLONG_MAX - mem_size, E820_RAM, 1);

	return 0;
}
early_param("mem", parse_memopt);

static int __init parse_memmap_opt(char *p)
{
	char *oldp;
	u64 start_at, mem_size;

	if (!p)
		return -EINVAL;

	if (!strncmp(p, "exactmap", 8)) {
#ifdef CONFIG_CRASH_DUMP
		/*
		 * If we are doing a crash dump, we still need to know
		 * the real mem size before original memory map is
		 * reset.
		 */
		saved_max_pfn = e820_end_of_ram_pfn();
#endif
		e820.nr_map = 0;
		userdef = 1;
		return 0;
	}

	oldp = p;
	mem_size = memparse(p, &p);
	if (p == oldp)
		return -EINVAL;

	userdef = 1;
	if (*p == '@') {
		start_at = memparse(p+1, &p);
		e820_add_region(start_at, mem_size, E820_RAM);
	} else if (*p == '#') {
		start_at = memparse(p+1, &p);
		e820_add_region(start_at, mem_size, E820_ACPI);
	} else if (*p == '$') {
		start_at = memparse(p+1, &p);
		e820_add_region(start_at, mem_size, E820_RESERVED);
	} else
		e820_remove_range(mem_size, ULLONG_MAX - mem_size, E820_RAM, 1);

	return *p == '\0' ? 0 : -EINVAL;
}
early_param("memmap", parse_memmap_opt);

void __init finish_e820_parsing(void)
{
	if (userdef) {
		int nr = e820.nr_map;

		if (sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &nr) < 0)
			early_panic("Invalid user supplied memory map");
		e820.nr_map = nr;

		printk(KERN_INFO "user-defined physical RAM map:\n");
		e820_print_map("user");
	}
}

static inline const char *e820_type_to_string(int e820_type)
{
	switch (e820_type) {
	case E820_RESERVED_KERN:
	case E820_RAM:	return "System RAM";
	case E820_ACPI:	return "ACPI Tables";
	case E820_NVS:	return "ACPI Non-volatile Storage";
	case E820_UNUSABLE:	return "Unusable memory";