s390/sclp: do not use static sccbs
authorGerald Schaefer <gerald.schaefer@de.ibm.com>
Mon, 8 Apr 2019 10:49:58 +0000 (12:49 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 29 Apr 2019 08:47:10 +0000 (10:47 +0200)
The sccbs for init/read/sdias/early have to be located below 2 GB, and
they are currently defined as a static buffer.

With a relocatable kernel that could reside at any place in memory, this
will no longer guarantee the location below 2 GB, so use a dynamic
GFP_DMA allocation instead.

The sclp_early_sccb buffer needs special handling, as it can be used
very early, and by both the decompressor and also the decompressed
kernel. Therefore, a fixed 4 KB buffer is introduced at 0x11000, the
former PARMAREA_END. The new PARMAREA_END is now 0x12000, and it is
renamed to HEAD_END, as it is rather the end of head.S and not the end
of the parmarea.

Signed-off-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Reviewed-by: Philipp Rudo <prudo@linux.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/boot/head.S
arch/s390/include/asm/setup.h
arch/s390/kernel/machine_kexec_file.c
arch/s390/kernel/setup.c
drivers/s390/char/sclp.c
drivers/s390/char/sclp.h
drivers/s390/char/sclp_early.c
drivers/s390/char/sclp_early_core.c
drivers/s390/char/sclp_sdias.c

index d585c4d..c6b4b4c 100644 (file)
@@ -336,4 +336,7 @@ ENTRY(startup_kdump)
        .byte   "root=/dev/ram0 ro"
        .byte   0
 
-       .org    0x11000
+       .org    EARLY_SCCB_OFFSET
+       .fill   4096
+
+       .org    HEAD_END
index b603cc0..d202756 100644 (file)
 #define EP_OFFSET              0x10008
 #define EP_STRING              "S390EP"
 #define PARMAREA               0x10400
-#define PARMAREA_END           0x11000
+#define EARLY_SCCB_OFFSET      0x11000
+#define HEAD_END               0x12000
+
+#define EARLY_SCCB_SIZE                PAGE_SIZE
 
 /*
  * Machine features detected in early.c
index 42c23a5..fbdd3ea 100644 (file)
@@ -337,10 +337,8 @@ int arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
         * load memory in head.S will be accessed, e.g. to register the next
         * command line. If the next kernel were smaller the current kernel
         * will panic at load.
-        *
-        * 0x11000 = sizeof(head.S)
         */
-       if (buf_len < 0x11000)
+       if (buf_len < HEAD_END)
                return -ENOEXEC;
 
        return kexec_image_probe_default(image, buf, buf_len);
index ffc8752..94efb1e 100644 (file)
@@ -829,7 +829,7 @@ static void __init reserve_kernel(void)
 {
        unsigned long start_pfn = PFN_UP(__pa(_end));
 
-       memblock_reserve(0, PARMAREA_END);
+       memblock_reserve(0, HEAD_END);
        memblock_reserve((unsigned long)_stext, PFN_PHYS(start_pfn)
                         - (unsigned long)_stext);
 }
index e9aa71c..d2ab3f0 100644 (file)
@@ -45,8 +45,8 @@ static struct list_head sclp_req_queue;
 /* Data for read and and init requests. */
 static struct sclp_req sclp_read_req;
 static struct sclp_req sclp_init_req;
-static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
-static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
+static void *sclp_read_sccb;
+static struct init_sccb *sclp_init_sccb;
 
 /* Suspend request */
 static DECLARE_COMPLETION(sclp_request_queue_flushed);
@@ -753,9 +753,8 @@ EXPORT_SYMBOL(sclp_remove_processed);
 static inline void
 __sclp_make_init_req(sccb_mask_t receive_mask, sccb_mask_t send_mask)
 {
-       struct init_sccb *sccb;
+       struct init_sccb *sccb = sclp_init_sccb;
 
-       sccb = (struct init_sccb *) sclp_init_sccb;
        clear_page(sccb);
        memset(&sclp_init_req, 0, sizeof(struct sclp_req));
        sclp_init_req.command = SCLP_CMDW_WRITE_EVENT_MASK;
@@ -782,7 +781,7 @@ static int
 sclp_init_mask(int calculate)
 {
        unsigned long flags;
-       struct init_sccb *sccb = (struct init_sccb *) sclp_init_sccb;
+       struct init_sccb *sccb = sclp_init_sccb;
        sccb_mask_t receive_mask;
        sccb_mask_t send_mask;
        int retry;
@@ -1175,6 +1174,9 @@ sclp_init(void)
        if (sclp_init_state != sclp_init_state_uninitialized)
                goto fail_unlock;
        sclp_init_state = sclp_init_state_initializing;
+       sclp_read_sccb = (void *) __get_free_page(GFP_ATOMIC | GFP_DMA);
+       sclp_init_sccb = (void *) __get_free_page(GFP_ATOMIC | GFP_DMA);
+       BUG_ON(!sclp_read_sccb || !sclp_init_sccb);
        /* Set up variables */
        INIT_LIST_HEAD(&sclp_req_queue);
        INIT_LIST_HEAD(&sclp_reg_list);
@@ -1207,6 +1209,8 @@ fail_unregister_reboot_notifier:
        unregister_reboot_notifier(&sclp_reboot_notifier);
 fail_init_state_uninitialized:
        sclp_init_state = sclp_init_state_uninitialized;
+       free_page((unsigned long) sclp_read_sccb);
+       free_page((unsigned long) sclp_init_sccb);
 fail_unlock:
        spin_unlock_irqrestore(&sclp_lock, flags);
        return rc;
index 043f32b..28b4339 100644 (file)
@@ -321,7 +321,7 @@ extern int sclp_console_drop;
 extern unsigned long sclp_console_full;
 extern bool sclp_mask_compat_mode;
 
-extern char sclp_early_sccb[PAGE_SIZE];
+extern char *sclp_early_sccb;
 
 void sclp_early_wait_irq(void);
 int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb);
index fdad2a9..6c90aa7 100644 (file)
@@ -147,7 +147,7 @@ static void __init sclp_early_console_detect(struct init_sccb *sccb)
 
 void __init sclp_early_detect(void)
 {
-       void *sccb = &sclp_early_sccb;
+       void *sccb = sclp_early_sccb;
 
        sclp_early_facilities_detect(sccb);
        sclp_early_init_core_info(sccb);
index 387c114..7737470 100644 (file)
@@ -16,7 +16,7 @@
 
 static struct read_info_sccb __bootdata(sclp_info_sccb);
 static int __bootdata(sclp_info_sccb_valid);
-char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(.data);
+char *sclp_early_sccb = (char *) EARLY_SCCB_OFFSET;
 int sclp_init_state __section(.data) = sclp_init_state_uninitialized;
 /*
  * Used to keep track of the size of the event masks. Qemu until version 2.11
@@ -91,8 +91,8 @@ static void sclp_early_print_lm(const char *str, unsigned int len)
        struct mto *mto;
        struct go *go;
 
-       sccb = (struct write_sccb *) &sclp_early_sccb;
-       end = (unsigned char *) sccb + sizeof(sclp_early_sccb) - 1;
+       sccb = (struct write_sccb *) sclp_early_sccb;
+       end = (unsigned char *) sccb + EARLY_SCCB_SIZE - 1;
        memset(sccb, 0, sizeof(*sccb));
        ptr = (unsigned char *) &sccb->msg.mdb.mto;
        offset = 0;
@@ -139,9 +139,9 @@ static void sclp_early_print_vt220(const char *str, unsigned int len)
 {
        struct vt220_sccb *sccb;
 
-       sccb = (struct vt220_sccb *) &sclp_early_sccb;
-       if (sizeof(*sccb) + len >= sizeof(sclp_early_sccb))
-               len = sizeof(sclp_early_sccb) - sizeof(*sccb);
+       sccb = (struct vt220_sccb *) sclp_early_sccb;
+       if (sizeof(*sccb) + len >= EARLY_SCCB_SIZE)
+               len = EARLY_SCCB_SIZE - sizeof(*sccb);
        memset(sccb, 0, sizeof(*sccb));
        memcpy(&sccb->msg.data, str, len);
        sccb->header.length = sizeof(*sccb) + len;
@@ -199,7 +199,7 @@ static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
        BUILD_BUG_ON(sizeof(struct init_sccb) > PAGE_SIZE);
 
        *have_linemode = *have_vt220 = 0;
-       sccb = (struct init_sccb *) &sclp_early_sccb;
+       sccb = (struct init_sccb *) sclp_early_sccb;
        receive_mask = disable ? 0 : EVTYP_OPCMD_MASK;
        send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK;
        rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask);
@@ -304,7 +304,7 @@ int __init sclp_early_get_hsa_size(unsigned long *hsa_size)
 void __weak __init add_mem_detect_block(u64 start, u64 end) {}
 int __init sclp_early_read_storage_info(void)
 {
-       struct read_storage_sccb *sccb = (struct read_storage_sccb *)&sclp_early_sccb;
+       struct read_storage_sccb *sccb = (struct read_storage_sccb *)sclp_early_sccb;
        int rc, id, max_id = 0;
        unsigned long rn, rzm;
        sclp_cmdw_t command;
@@ -320,8 +320,8 @@ int __init sclp_early_read_storage_info(void)
        rzm <<= 20;
 
        for (id = 0; id <= max_id; id++) {
-               memset(sclp_early_sccb, 0, sizeof(sclp_early_sccb));
-               sccb->header.length = sizeof(sclp_early_sccb);
+               memset(sclp_early_sccb, 0, EARLY_SCCB_SIZE);
+               sccb->header.length = EARLY_SCCB_SIZE;
                command = SCLP_CMDW_READ_STORAGE_INFO | (id << 8);
                rc = sclp_early_cmd(command, sccb);
                if (rc)
index 8e0b69a..13f97fd 100644 (file)
@@ -29,7 +29,7 @@ static struct sclp_register sclp_sdias_register = {
        .send_mask = EVTYP_SDIAS_MASK,
 };
 
-static struct sdias_sccb sccb __attribute__((aligned(4096)));
+static struct sdias_sccb *sclp_sdias_sccb;
 static struct sdias_evbuf sdias_evbuf;
 
 static DECLARE_COMPLETION(evbuf_accepted);
@@ -58,6 +58,7 @@ static void sdias_callback(struct sclp_req *request, void *data)
 
 static int sdias_sclp_send(struct sclp_req *req)
 {
+       struct sdias_sccb *sccb = sclp_sdias_sccb;
        int retries;
        int rc;
 
@@ -78,16 +79,16 @@ static int sdias_sclp_send(struct sclp_req *req)
                        continue;
                }
                /* if not accepted, retry */
-               if (!(sccb.evbuf.hdr.flags & 0x80)) {
+               if (!(sccb->evbuf.hdr.flags & 0x80)) {
                        TRACE("sclp request failed: flags=%x\n",
-                             sccb.evbuf.hdr.flags);
+                             sccb->evbuf.hdr.flags);
                        continue;
                }
                /*
                 * for the sync interface the response is in the initial sccb
                 */
                if (!sclp_sdias_register.receiver_fn) {
-                       memcpy(&sdias_evbuf, &sccb.evbuf, sizeof(sdias_evbuf));
+                       memcpy(&sdias_evbuf, &sccb->evbuf, sizeof(sdias_evbuf));
                        TRACE("sync request done\n");
                        return 0;
                }
@@ -104,23 +105,24 @@ static int sdias_sclp_send(struct sclp_req *req)
  */
 int sclp_sdias_blk_count(void)
 {
+       struct sdias_sccb *sccb = sclp_sdias_sccb;
        struct sclp_req request;
        int rc;
 
        mutex_lock(&sdias_mutex);
 
-       memset(&sccb, 0, sizeof(sccb));
+       memset(sccb, 0, sizeof(*sccb));
        memset(&request, 0, sizeof(request));
 
-       sccb.hdr.length = sizeof(sccb);
-       sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
-       sccb.evbuf.hdr.type = EVTYP_SDIAS;
-       sccb.evbuf.event_qual = SDIAS_EQ_SIZE;
-       sccb.evbuf.data_id = SDIAS_DI_FCP_DUMP;
-       sccb.evbuf.event_id = 4712;
-       sccb.evbuf.dbs = 1;
+       sccb->hdr.length = sizeof(*sccb);
+       sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
+       sccb->evbuf.hdr.type = EVTYP_SDIAS;
+       sccb->evbuf.event_qual = SDIAS_EQ_SIZE;
+       sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
+       sccb->evbuf.event_id = 4712;
+       sccb->evbuf.dbs = 1;
 
-       request.sccb = &sccb;
+       request.sccb = sccb;
        request.command = SCLP_CMDW_WRITE_EVENT_DATA;
        request.status = SCLP_REQ_FILLED;
        request.callback = sdias_callback;
@@ -130,8 +132,8 @@ int sclp_sdias_blk_count(void)
                pr_err("sclp_send failed for get_nr_blocks\n");
                goto out;
        }
-       if (sccb.hdr.response_code != 0x0020) {
-               TRACE("send failed: %x\n", sccb.hdr.response_code);
+       if (sccb->hdr.response_code != 0x0020) {
+               TRACE("send failed: %x\n", sccb->hdr.response_code);
                rc = -EIO;
                goto out;
        }
@@ -163,30 +165,31 @@ out:
  */
 int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
 {
+       struct sdias_sccb *sccb = sclp_sdias_sccb;
        struct sclp_req request;
        int rc;
 
        mutex_lock(&sdias_mutex);
 
-       memset(&sccb, 0, sizeof(sccb));
+       memset(sccb, 0, sizeof(*sccb));
        memset(&request, 0, sizeof(request));
 
-       sccb.hdr.length = sizeof(sccb);
-       sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
-       sccb.evbuf.hdr.type = EVTYP_SDIAS;
-       sccb.evbuf.hdr.flags = 0;
-       sccb.evbuf.event_qual = SDIAS_EQ_STORE_DATA;
-       sccb.evbuf.data_id = SDIAS_DI_FCP_DUMP;
-       sccb.evbuf.event_id = 4712;
-       sccb.evbuf.asa_size = SDIAS_ASA_SIZE_64;
-       sccb.evbuf.event_status = 0;
-       sccb.evbuf.blk_cnt = nr_blks;
-       sccb.evbuf.asa = (unsigned long)dest;
-       sccb.evbuf.fbn = start_blk;
-       sccb.evbuf.lbn = 0;
-       sccb.evbuf.dbs = 1;
-
-       request.sccb     = &sccb;
+       sccb->hdr.length = sizeof(*sccb);
+       sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
+       sccb->evbuf.hdr.type = EVTYP_SDIAS;
+       sccb->evbuf.hdr.flags = 0;
+       sccb->evbuf.event_qual = SDIAS_EQ_STORE_DATA;
+       sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
+       sccb->evbuf.event_id = 4712;
+       sccb->evbuf.asa_size = SDIAS_ASA_SIZE_64;
+       sccb->evbuf.event_status = 0;
+       sccb->evbuf.blk_cnt = nr_blks;
+       sccb->evbuf.asa = (unsigned long)dest;
+       sccb->evbuf.fbn = start_blk;
+       sccb->evbuf.lbn = 0;
+       sccb->evbuf.dbs = 1;
+
+       request.sccb     = sccb;
        request.command  = SCLP_CMDW_WRITE_EVENT_DATA;
        request.status   = SCLP_REQ_FILLED;
        request.callback = sdias_callback;
@@ -196,8 +199,8 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
                pr_err("sclp_send failed: %x\n", rc);
                goto out;
        }
-       if (sccb.hdr.response_code != 0x0020) {
-               TRACE("copy failed: %x\n", sccb.hdr.response_code);
+       if (sccb->hdr.response_code != 0x0020) {
+               TRACE("copy failed: %x\n", sccb->hdr.response_code);
                rc = -EIO;
                goto out;
        }
@@ -256,6 +259,8 @@ int __init sclp_sdias_init(void)
 {
        if (ipl_info.type != IPL_TYPE_FCP_DUMP)
                return 0;
+       sclp_sdias_sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
+       BUG_ON(!sclp_sdias_sccb);
        sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));
        debug_register_view(sdias_dbf, &debug_sprintf_view);
        debug_set_level(sdias_dbf, 6);
@@ -264,6 +269,7 @@ int __init sclp_sdias_init(void)
        if (sclp_sdias_init_async() == 0)
                goto out;
        TRACE("init failed\n");
+       free_page((unsigned long) sclp_sdias_sccb);
        return -ENODEV;
 out:
        TRACE("init done\n");